diff --git a/assets/blocks.psd b/assets/blocks.psd index 950f8f813..12c4eac52 100644 Binary files a/assets/blocks.psd and b/assets/blocks.psd differ diff --git a/build.gradle b/build.gradle index f7ea8a62d..5a42b5f34 100644 --- a/build.gradle +++ b/build.gradle @@ -45,12 +45,13 @@ def getGitRef() { if (System.getenv("BUILD_NUMBER") != null) version += ".${System.getenv("BUILD_NUMBER")}" -else - version += "-" + getGitRef() if (config.oc.subversion != null && config.oc.subversion != "") version += "-${config.oc.subversion}" +if (System.getenv("BUILD_NUMBER") == null) + version += "+" + getGitRef() + ext.simpleVersion = version version = "MC${config.minecraft.version}-${project.version}" @@ -238,6 +239,7 @@ idea.module.scopes.PROVIDED.plus += [configurations.provided] sourceSets { main { scala { + exclude 'li/cil/oc/integration/agricraft/**' exclude 'li/cil/oc/integration/appeng/**' exclude 'li/cil/oc/integration/bloodmagic/**' exclude 'li/cil/oc/integration/bluepower/**' diff --git a/build.properties b/build.properties index cefd032a6..cf41e0f4c 100644 --- a/build.properties +++ b/build.properties @@ -1,7 +1,7 @@ minecraft.version=1.8 forge.version=11.14.3.1450 -oc.version=1.5.15 +oc.version=1.5.16 oc.subversion= ae2.version=rv2-beta-26 diff --git a/src/main/java/li/cil/oc/api/API.java b/src/main/java/li/cil/oc/api/API.java index 051bcb5d8..471ef36e7 100644 --- a/src/main/java/li/cil/oc/api/API.java +++ b/src/main/java/li/cil/oc/api/API.java @@ -12,7 +12,7 @@ import li.cil.oc.api.detail.*; */ public class API { public static final String ID_OWNER = "OpenComputers|Core"; - public static final String VERSION = "5.5.3"; + public static final String VERSION = "5.5.4"; public static DriverAPI driver = null; public static FileSystemAPI fileSystem = null; diff --git a/src/main/java/li/cil/oc/api/driver/item/MutableProcessor.java b/src/main/java/li/cil/oc/api/driver/item/MutableProcessor.java new file mode 100644 index 000000000..04cc07634 --- /dev/null +++ b/src/main/java/li/cil/oc/api/driver/item/MutableProcessor.java @@ -0,0 +1,27 @@ +package li.cil.oc.api.driver.item; + +import li.cil.oc.api.machine.Architecture; +import net.minecraft.item.ItemStack; + +/** + * May be implemented in processor drivers of processors that can be reconfigured. + *

+ * This is the case for OC's built-in CPUs, for example, which can be reconfigured + * to any registered architecture. It a CPU has such a driver, it may also be + * reconfigured by the machine it is running in (e.g. in the Lua case via + * computer.setArchitecture). + */ +public interface MutableProcessor extends Processor { + /** + * Get a list of all architectures supported by this processor. + */ + java.util.Collection> allArchitectures(); + + /** + * Set the architecture to use for the specified processor. + * + * @param stack the processor to set the architecture for. + * @param architecture the architecture to use on the processor. + */ + void setArchitecture(ItemStack stack, Class architecture); +} diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 863dbad11..09b6b656d 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -126,10 +126,10 @@ opencomputers { # happen. startupDelay: 0.25 - # The maximum size of the byte array that can be stored on EEPROMs. + # The maximum size of the byte array that can be stored on EEPROMs as executable data.. eepromSize: 4096 - # The maximum size of the byte array that can be stored on EEPROMs. + # The maximum size of the byte array that can be stored on EEPROMs as configuration data. eepromDataSize: 256 # The number of components the different CPU tiers support. This list @@ -767,6 +767,10 @@ opencomputers { # Per-byte cost for ECDSA operation is controlled by `complex` value, # because data is hashed with SHA256 before signing/verifying dataCardAsymmetric: 10.0 + + # Energy required for one transposer operation (regardless of the number + # of items / fluid volume moved). + transposer: 1 } # The rate at which different blocks accept external power. All of these @@ -1191,6 +1195,11 @@ opencomputers { # properties, i.e. througput, frequency and buffer size. # Valid values are: 0 = none, 1 = tier 1, 2 = tier 2, 3 = tier 3. serverRackSwitchTier: 1 + + # Enforced delay when changing a redstone emitting component's output, + # such as the redstone card and redstone I/O block. Lowering this can + # have very negative impact on server TPS, so beware. + redstoneDelay: 0.1 } # Settings for mod integration (the mod previously known as OpenComponents). diff --git a/src/main/resources/assets/opencomputers/blockstates/relay.json b/src/main/resources/assets/opencomputers/blockstates/relay.json new file mode 100644 index 000000000..bb0444909 --- /dev/null +++ b/src/main/resources/assets/opencomputers/blockstates/relay.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal": { "model": "opencomputers:relay" } + } +} diff --git a/src/main/resources/assets/opencomputers/blockstates/transposer.json b/src/main/resources/assets/opencomputers/blockstates/transposer.json new file mode 100644 index 000000000..32bd3dad6 --- /dev/null +++ b/src/main/resources/assets/opencomputers/blockstates/transposer.json @@ -0,0 +1,5 @@ +{ + "variants": { + "normal": { "model": "opencomputers:transposer" } + } +} diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md b/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md index 5dde0da28..78bacc26c 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/accessPoint.md @@ -2,6 +2,8 @@ ![AAA](oredict:oc:accessPoint) +*This block is deprecated and will be removed in a future version.* Craft it into a [relay](relay.md) to avoid losing it. + The access point is the wireless version of the [switch](switch.md). It can be used to separate subnetworks so that machines in them will not see [components](../general/computer.md) in other networks, while still allowing to send network messages to the machines in other networks. In addition to that, this block can act as a repeater: it can re-send wired messages as wired messages to other devices; or wireless messages as wired or wireless messages. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/index.md b/src/main/resources/assets/opencomputers/doc/en_US/block/index.md index a33f40557..26a47e734 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/index.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/index.md @@ -36,10 +36,9 @@ Keep in mind that some of these may not be available, depending on the recipe se * [Disassembler](disassembler.md) ## Networking -* [Access Point](accessPoint.md) * [Cable](cable.md) * [Net Splitter](netSplitter.md) -* [Switch](switch.md) +* [Relay](relay.md) ## Power management * [Capacitor](capacitor.md) diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md b/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md index 230e4484f..f6c02a752 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/netSplitter.md @@ -2,6 +2,6 @@ ![*.net *.split](oredict:oc:netSplitter) -The net splitter is a device that allows controlling connectivity between subnetworks. Unlike the [switch](switch.md) or [power converter](powerConverter.md) it directly connects adjacent subnetworks, i.e. components can be accessed. Each side's connectivity can be toggled using a wrench (e.g. the [scrench](../item/wrench.md)). When a redstone signal is applied to the net splitter, all sides' connectivity is inverted. +The net splitter is a device that allows controlling connectivity between subnetworks. Unlike the [relay](relay.md) or [power converter](powerConverter.md) it directly connects adjacent subnetworks, i.e. components can be accessed. Each side's connectivity can be toggled using a wrench (e.g. the [scrench](../item/wrench.md)). When a redstone signal is applied to the net splitter, all sides' connectivity is inverted. This block can therefore be used to toggle connectivity to certain parts of a component network. Use a [redstone I/O block](redstone.md) or [redstone cards](../item/redstoneCard1.md) to automate the net splitter. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/relay.md b/src/main/resources/assets/opencomputers/doc/en_US/block/relay.md new file mode 100644 index 000000000..212369838 --- /dev/null +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/relay.md @@ -0,0 +1,13 @@ +# Relay + +![Building bridges.](oredict:oc:relay) + +The relay can be used to allow different subnetworks to send network messages to each other, without exposing components to [computers](../general/computer.md) in other networks. Keeping components local is usually a good idea, to avoid [computers](../general/computer.md) using the wrong [screen](screen1.md) or to avoid component overflows to happen (causing [computers](../general/computer.md) to crash and refuse to boot up). + +The relay can be upgraded by inserting a [wireless network card](../item/wlanCard.md) to also relay messages wirelessly. Wireless messages can be received and relayed by other relays with a wireless network card, or by [computers](../general/computer.md) with a wireless network card. + +Alternatively the relay can be upgraded using [linked cards](../item/linkedCard.md). In this case it will forward messages through the tunnel provided by the linked card, too; at the usual cost, so make sure the relay is sufficiently powered. + +Relays do *not* keep track of which packets they forwarded recently, so avoid cycles in your network or you may receive the same packet multiple times. Due to the limited buffer size of relays, sending messages too frequently will result in packet loss. You can upgrade your relays to increase the speed with which they relay messages, as well as their internal message queue size. + +Packets are only re-sent a certain number of times, so chaining an arbitrary number of relays is not possible. By default, a packet will be re-sent up to five times. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md b/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md index 49f1df835..5630023b4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/serverRack.md @@ -6,4 +6,4 @@ A server rack houses up to four [servers](../item/server1.md). A [server](../ite Each [server](../item/server1.md) in a server rack can only communicate with one "face" of the server rack at a time - or none at all. Which side each [server](../item/server1.md) is connected to can be configured in the server rack's GUI. Beware that the sides are from the point of view of the server rack, i.e. if you are looking at the front of the server rack, `sides.right` will be to your left and vice versa. -Server racks act as [switch](switch.md) and [power distributor](powerDistributor.md) in one. The switch mode of the server rack can be configured in its GUI, with the two options being internal and external. In external mode the server rack will behave like a normal [switch](switch.md). In internal mode, messages are only passed to the [servers](../item/server1.md) in the rack, and will not be automatically relayed to the other faces of the rack. [Servers](../item/server1.md) will still be able to send messages to each other. This allows using server racks as advanced [switches](switch.md) that can perform filter and mapping operations, for example. +Server racks act as [relay](relay.md) and [power distributor](powerDistributor.md) in one. The switch mode of the server rack can be configured in its GUI, with the two options being internal and external. In external mode the server rack will behave like a normal [relay](relay.md). In internal mode, messages are only passed to the [servers](../item/server1.md) in the rack, and will not be automatically relayed to the other faces of the rack. [Servers](../item/server1.md) will still be able to send messages to each other. This allows using server racks as advanced [relays](relay.md) that can perform filter and mapping operations, for example. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md b/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md index a607e0cfd..35ad59fd4 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/switch.md @@ -2,6 +2,8 @@ ![Building bridges.](oredict:oc:switch) +*This block is deprecated and will be removed in a future version.* Craft it into a [relay](relay.md) to avoid losing it. + The switch can be used to allow different subnetworks to send network messages to each other, without exposing components to [computers](../general/computer.md) in other networks. Keeping components local is usually a good idea, to avoid [computers](../general/computer.md) using the wrong [screen](screen1.md) or to avoid component overflows to happen (causing [computers](../general/computer.md) to crash and refuse to boot up). There is also a wireless variation of this block, called the [access point](accessPoint.md), which will also relay messages wirelessly. Wireless messages can be received and relayed by other [access points](accessPoint.md), or by [computers](../general/computer.md) with a [wireless network card](../item/wlanCard.md). diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/transposer.md b/src/main/resources/assets/opencomputers/doc/en_US/block/transposer.md new file mode 100644 index 000000000..ccc3e07ff --- /dev/null +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/transposer.md @@ -0,0 +1,9 @@ +# Transposer + +![Such a poser.](oredict:oc:transposer) + +The transposer bridges the gap between redstone controlled hoppers and [robots](robot.md), allowing [computer](../general/computer.md)-controlled transferral of items and fluids between adjacent blocks. + +*Note that this block has no internal inventory.* + +Besides moving things around, it can also be used to inspect the contents of the adjacent inventories, like an [adapter](adapter.md) with an [inventory controller upgrade](../item/inventoryControllerUpgrade.md) could, and the contents of adjacent tanks, like and adapter with a [tank controller upgrade](../item/tankControllerUpgrade.md) could. diff --git a/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md b/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md index 3fbd8a6c4..fc730e035 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/item/lanCard.md @@ -2,4 +2,4 @@ ![Enter the network.](oredict:oc:lanCard) -The network card allows [computers](../general/computer.md) to send and receive network messages. Messages (or packets) can be broadcasted to all receiving nodes in a subnetwork, or sent to a specific node with a specified address. [Switches](../block/switch.md) and [access points](../block/accessPoint.md) can be used to bridge multiple subnetworks by relaying messages between the subnetworks they are connected to. It is also possible to send a targeted message if the receiver is in another subnetwork, if the networks are connected via one or more [switches](../block/switch.md). +The network card allows [computers](../general/computer.md) to send and receive network messages. Messages (or packets) can be broadcasted to all receiving nodes in a subnetwork, or sent to a specific node with a specified address. [Relays](../block/relay.md) can be used to bridge multiple subnetworks by relaying messages between the subnetworks they are connected to. It is also possible to send a targeted message if the receiver is in another subnetwork, if the networks are connected via one or more [relays](../block/relay.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/accessPoint.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/accessPoint.md index 744abec69..faa4a62bf 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_RU/block/accessPoint.md +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/accessPoint.md @@ -2,6 +2,8 @@ ![AAA](oredict:oc:accessPoint) +*Этот блок устарел и будет удален в следующих версиях.* Замените его на [ретранслятор](relay.md). + Точки доступа, это беспроводные версии [коммутаторов](switch.md). Они могут быть использованы, когда требуется разделить подсети, чтобы устройства не видели [компоненты](../general/computer.md) в других сетях, однако сохраняя при этом возможность передачи сообщений между подсетями. В дополнение к этому, точки доступа могут использоваться как повторители: они могут перенаправлять сообщения из проводной линии другим устройствам; или беспроводные сообщения как проводные, так и беспроводные. diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/index.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/index.md index a1e604531..ce0ebf1d9 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_RU/block/index.md +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/index.md @@ -39,6 +39,7 @@ * [Точка доступа](accessPoint.md) * [Кабель](cable.md) * [Сетевой переключатель](netSplitter.md) +* [Ретранслятор](relay.md) * [Коммутатор](switch.md) ## Управление питанием diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/netSplitter.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/netSplitter.md index 37e4bc296..e3725dcb5 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_RU/block/netSplitter.md +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/netSplitter.md @@ -2,6 +2,6 @@ ![*.net *.split](oredict:oc:netSplitter) -Сетевой переключатель позволяет контролировать соединение между подсетями. В отличие от [коммутатора](switch.md) или [конвертера энергии](powerConverter.md) позволяет непосредственно соединить подсети, делая при этом доступными все компоненты. Соединение каждой стороны переключается [ключем](../item/wrench.md). При подаче редстоун-сигнала все соединения инвертируются. +Сетевой переключатель позволяет контролировать соединение между подсетями. В отличие от [ретранслятора](relay.md) или [конвертера энергии](powerConverter.md) позволяет непосредственно соединить подсети, делая при этом доступными все компоненты. Соединение каждой стороны переключается [ключем](../item/wrench.md). При подаче редстоун-сигнала все соединения инвертируются. Таким образом, этот блок может быть использован для переключения соединения определенных компонентов сети. Используйте [редстоун-I/O](redstone.md) или [редстоун карты](../item/redstoneCard1.md) для автоматизации сетевого переключателя. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/relay.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/relay.md new file mode 100644 index 000000000..64caecb9f --- /dev/null +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/relay.md @@ -0,0 +1,13 @@ +# Ретранслятор + +![Строит мосты.](oredict:oc:relay) + +Ретранслятор используется для передачи сообщений между несколькими подсетями, изолируя компоненты [компьютеров](../general/computer.md) в других сетях. Сохранение компонентов, как правило, хорошая идея, что не позволяет [компьютерам](../general/computer.md) использовать неверный [монитор](screen1.md) или не позволяет перенагружать их (в результате чего [компьютеры](../general/computer.md) выключатся и отказываются загружаться). + +Может быть улучшен добавлением [беспроводной сетевой карты](../item/wlanCard.md), что позволит ретранслировать беспроводные сообщения. Сообщения, переданные по беспроводной линии могут быть получены или перенаправлены другими ретрансляторами или [компьютерами](../general/computer.md) с [беспроводной сетевой картой](../item/wlanCard.md). + +Также ретранслятор может быть улучшен с помощью [соединенной карты](../item/linkedCard.md). Это позволит передавать сообщения внутри туннеля, предоставленного картой, также; с обычными затратами энергии, поэтому убедитесь, что на ретранслятор поступает нужное количество энергии. + +Ретрансляторы *не* отслеживают, какие пакеты и куда они передали, поэтому для в сети могут образовываться петли или вы можете получать одно сообщение несколько раз. Из-за ограниченного буфера сообщений коммутатора, частое отправление сообщений приводит к их потере. Вы можете улучшить ретранслятор для увеличения скорости обработки сообщений, а также увеличения размера сообщений. + +Сообщения, могут перенаправлены всего несколько раз, поэтому цепочки с произвольным количеством ретрансляторов невозможны. По умолчанию, сообщение может быть перенаправлено пять раз. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/serverRack.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/serverRack.md index 4a5af4ef2..850d739ed 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_RU/block/serverRack.md +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/serverRack.md @@ -6,4 +6,4 @@ Каждый [сервер](../item/server1.md) в серверной стойке может взаимодействовать только с одной "стороной" стойки или ни с какой. К какой стороне, какой [сервер](../item/server1.md) подключен, настраивается в интерфейсе стойки. Будьте внимательны, стороны считаются относительно самой стойки, например, если вы смотрите на стойку спереди, то `правая сторона` стойки для вас будет слева. -Серверные стойки взаимодействуют с [коммутаторами](switch.md) и [распределителями энергии](powerDistributor.md). Переключатель режимов работы стойки, может быть настроен в интерфейсе самой стойки, он имеет 2 режима: внешний и внутренний. Во внешнем режиме сервер будет работать как обычный [коммутатор](switch.md). Во внутреннем режиме, сообщения будут передаваться только к [серверам](../item/server1.md) в стойке и не будут автоматически связаны со сторонами стойки. [Серверы](../item/server1.md) все также будут иметь возможность передачи сообщений друг другу. Это позволяет использовать серверные стойки как продвинутые [коммутаторы](switch.md) для операций фильтрации и направления данных, например. +Серверные стойки взаимодействуют с [ретрансляторами](relay.md) и [распределителями энергии](powerDistributor.md). Переключатель режимов работы стойки, может быть настроен в интерфейсе самой стойки, он имеет 2 режима: внешний и внутренний. Во внешнем режиме сервер будет работать как обычный [ретранслятор](relay.md). Во внутреннем режиме, сообщения будут передаваться только к [серверам](../item/server1.md) в стойке и не будут автоматически связаны со сторонами стойки. [Серверы](../item/server1.md) все также будут иметь возможность передачи сообщений друг другу. Это позволяет использовать серверные стойки как продвинутые [ретрансляторы](relay.md) для операций фильтрации и направления данных, например. diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/switch.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/switch.md index 56445e17f..27b172988 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_RU/block/switch.md +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/switch.md @@ -2,6 +2,8 @@ ![Строит мосты.](oredict:oc:switch) +*Этот блок устарел и будет удален в следующих версиях.* Замените его на [ретранслятор](relay.md). + Коммутатор используется для передачи сообщений между несколькими подсетями, не используя компоненты [компьютеров](../general/computer.md) в других сетях. Сохранение компонентов, как правило, хорошая идея, что не позволяет [компьютерам](../general/computer.md) использовать неверный [монитор](screen1.md) или не позволяет перенагружать их (в результате чего [компьютеры](../general/computer.md) выключатся и отказываются загружаться). Также есть беспроводная версия коммутатора, называемая [точка доступа](accessPoint.md), с ее помощью сообщения передаются по беспроводной линии. Сообщения, переданные по беспроводной линии могут быть получены или перенаправлены другими [точками доступа](accessPoint.md) или [компьютерами](../general/computer.md) с [беспроводной сетевой картой](../item/wlanCard.md). diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/transposer.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/transposer.md new file mode 100644 index 000000000..ea9c615e0 --- /dev/null +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/transposer.md @@ -0,0 +1,9 @@ +# Транспозер + +![Такая проблемка.](oredict:oc:transposer) + +Транспозер заполняет пробел между воронками, контролируемыми редстоун-сигналом и [роботами](robot.md), позволяя [компьютерам](../general/computer.md) контролировать перемещение предметов и жидкостей между соседними блоками. + +*Данный блок не имеет внутреннего инвентаря.* + +Кроме перемещения блоков, он также может быть использован для проверки содержимого соседних инвентарей, подобно [адаптеру](adapter.md) с [контроллером инвентаря](../item/inventoryControllerUpgrade.md), и содержимого соседних хранилищ жидкости, подобно адаптеру с [контроллером жидкости](../item/tankControllerUpgrade.md). \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/item/lanCard.md b/src/main/resources/assets/opencomputers/doc/ru_RU/item/lanCard.md index 7094ef65b..6a25e7b07 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_RU/item/lanCard.md +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/item/lanCard.md @@ -2,4 +2,4 @@ ![Войди в сеть.](oredict:oc:lanCard) -Сетевая карта позволяет [компьютерам](../general/computer.md) отправлять и получать сетевые сообщения. Сообщения (или пакеты) будут отправлены всем принимающим устройствам в подсети или конкретной сетевой карте (после указания ее адреса). [Коммутаторы](../block/switch.md) и [точки доступа](../block/accessPoint.md) могут быть использованы для связи нескольких подсетей друг с другом, для передачи сообщений в них. Также возможно отправить письмо получателю, даже если он находится в другой подсети, если сеть подключена к одному или нескольким [коммутаторам](../block/switch.md). +Сетевая карта позволяет [компьютерам](../general/computer.md) отправлять и получать сетевые сообщения. Сообщения (или пакеты) будут отправлены всем принимающим устройствам в подсети или конкретной сетевой карте (при указании её адреса). [Ретрансляторы](../block/relay.md) могут быть использованы для связи нескольких подсетей друг с другом и передачи сообщений. Также возможно отправить письмо получателю, даже если он находится в другой подсети, если сеть подключена к одному или нескольким [ретрасляторами](../block/relay.md). diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 7091603c1..c53d25713 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -3,7 +3,7 @@ # Use [nl] to for a line break. # Blocks -tile.oc.accessPoint.name=Access Point +tile.oc.accessPoint.name=§cAccess Point§7 tile.oc.adapter.name=Adapter tile.oc.assembler.name=Electronics Assembler tile.oc.cable.name=Cable @@ -23,20 +23,22 @@ tile.oc.hologram2.name=Hologram Projector (Tier 2) tile.oc.keyboard.name=Keyboard tile.oc.microcontroller.name=Microcontroller tile.oc.motionSensor.name=Motion Sensor +tile.oc.netSplitter.name=Net Splitter tile.oc.powerConverter.name=Power Converter tile.oc.powerDistributor.name=Power Distributor tile.oc.print.name=3D Print tile.oc.printer.name=3D Printer tile.oc.raid.name=Raid tile.oc.redstone.name=Redstone I/O +tile.oc.relay.name=Relay tile.oc.robot.name=Robot tile.oc.robotAfterimage.name=Robot tile.oc.screen1.name=Screen (Tier 1) tile.oc.screen2.name=Screen (Tier 2) tile.oc.screen3.name=Screen (Tier 3) tile.oc.serverRack.name=Server Rack -tile.oc.switch.name=Switch -tile.oc.netSplitter.name=Net Splitter +tile.oc.switch.name=§cSwitch§7 +tile.oc.transposer.name=Transposer tile.oc.waypoint.name=Waypoint # Items @@ -235,6 +237,7 @@ oc:container.Disassembler=Disassembler oc:container.DiskDrive=Disk Drive oc:container.Printer=Printer oc:container.Raid=Raid +oc:container.Relay=Relay oc:container.Server=Server oc:container.ServerRack=Server Rack oc:container.Switch=Switch @@ -328,6 +331,7 @@ oc:tooltip.RedstoneCard.RedNet=§fRedNet§7 is §asupported§7. oc:tooltip.RedstoneCard.WirelessCBE=§fWireless Redstone (ChickenBones)§7 is §asupported§7. oc:tooltip.RedstoneCard.WirelessSV=§fWireless Redstone (SlimeVoid)§7 is §asupported§7. oc:tooltip.RedstoneCard=Allows reading and emitting redstone signals around the computer or robot. +oc:tooltip.Relay=Allows connecting different networks to each other. Only network messages will be passed along, components will not be visible through this. Use this to separate networks while still allowing communication using Network Cards, for example. oc:tooltip.Robot=Unlike computers, robots can move around and interact with the world much like a player can.[nl] §cCan not connect to external components.§7 # The underscore makes sure this isn't hidden with the rest of the tooltip. oc:tooltip.Robot_Level=§fLevel§7: §a%s§7 @@ -345,6 +349,7 @@ oc:tooltip.Tier=§8Tier %s oc:tooltip.NetSplitter=Acts as a dynamic connector. Connectivity of each side can be toggled by hitting it with a wrench. Connectivity of all sides can be inverted by applying a redstone signal. oc:tooltip.TooLong=Hold [§f%s§7] for a detailed tooltip. oc:tooltip.Transistor=A basic element in most other computer parts. It's a bit twisted, but it does the job. +oc:tooltip.Transposer=Allows automated transferral of items and fluids between adjacent inventories and fluid containers. oc:tooltip.UpgradeAngel=Allows robots to place blocks in thin air, even if there is no point of reference. oc:tooltip.UpgradeBattery=Increase the amount of energy a device can store, allowing it work longer without having to be recharged. [nl] Capacity: §f%s§7 oc:tooltip.UpgradeChunkloader=If a robot moves in a forest and no one is around to see it, does it really move? This upgrades makes sure it does. It keeps the chunk a device is in loaded, but continually consumes energy while active. diff --git a/src/main/resources/assets/opencomputers/lang/ru_RU.lang b/src/main/resources/assets/opencomputers/lang/ru_RU.lang index 6062721fd..37e306090 100644 --- a/src/main/resources/assets/opencomputers/lang/ru_RU.lang +++ b/src/main/resources/assets/opencomputers/lang/ru_RU.lang @@ -3,7 +3,7 @@ # Use [nl] to for a line break. # Blocks -tile.oc.accessPoint.name=Точка доступа +tile.oc.accessPoint.name=§cТочка доступа§7 tile.oc.adapter.name=Адаптер tile.oc.assembler.name=Сборщик роботов tile.oc.cable.name=Кабель @@ -23,6 +23,7 @@ tile.oc.hologram2.name=Голографический проектор (2-ой tile.oc.keyboard.name=Клавиатура tile.oc.microcontroller.name=Микроконтроллер tile.oc.motionSensor.name=Датчик движения +tile.oc.netSplitter.name=Сетевой переключатель tile.oc.powerConverter.name=Преобразователь энергии tile.oc.powerDistributor.name=Распределитель энергии tile.oc.print.name=3D модель @@ -35,8 +36,8 @@ tile.oc.screen1.name=Монитор (1-ый уровень) tile.oc.screen2.name=Монитор (2-ой уровень) tile.oc.screen3.name=Монитор (3-ий уровень) tile.oc.serverRack.name=Серверная стойка -tile.oc.switch.name=Коммутатор -tile.oc.netSplitter.name=Сетевой переключатель +tile.oc.switch.name=§cКоммутатор§7 +tile.oc.transposer.name=Транспозер tile.oc.waypoint.name=Путевая точка # Items @@ -232,6 +233,7 @@ oc:container.Disassembler=Разборщик oc:container.DiskDrive=Дисковод oc:container.Printer=Принтер oc:container.Raid=RAID +oc:container.Relay=Ретранслятор oc:container.Server=Сервер oc:container.ServerRack=Серверная стойка oc:container.Switch=Коммутатор @@ -325,6 +327,7 @@ oc:tooltip.RedstoneCard.RedNet=Мод §fRedNet§7 §aподдерживаетс oc:tooltip.RedstoneCard.WirelessCBE=Мод §fWireless Redstone (ChickenBones)§7 §aподдерживается§7. oc:tooltip.RedstoneCard.WirelessSV=Мод §fWireless Redstone (SlimeVoid)§7 §aподдерживается§7. oc:tooltip.RedstoneCard=Позволяет принимать и излучать сигналы красного камня вокруг компьютера или робота. +oc:tooltip.Relay=Позволяет соединять различные сети между собой. Передаются только сообщения, компоненты между сетями недоступны. Используйте его для разделения сетей с возможностью пересылать сообщения между ними. oc:tooltip.Robot=В отличие от компьютеров, роботы могут передвигаться и взаимодействовать с миром, как игрок.[nl] §cНе могут взаимодействовать с внешними компонентами.§7 # The underscore makes sure this isn't hidden with the rest of the tooltip. oc:tooltip.Robot_Level=§fУровень§7: §a%s§7. @@ -342,6 +345,7 @@ oc:tooltip.Tier=§8Уровень %s oc:tooltip.NetSplitter=Работает как переключатель. Соединение каждой стороны переключается ключем. При подаче сигнала красного камня все соединения инвертируются. oc:tooltip.TooLong=Удерживайте [§f%s§7], чтобы отобразить описание. oc:tooltip.Transistor=Базовый элемент для других частей компьютера. Он немного деформирован, но отлично выполняет свою работу. +oc:tooltip.Transposer=Позволяет автоматизировать перемещение блоков и жидкостей между соседними инвентарями и хранилищами жидкости. oc:tooltip.UpgradeAngel=Позволяет роботам размещать блоки в воздухе, даже если отсутствует точка опоры. oc:tooltip.UpgradeBattery=Увеличивает количество энергии, которую робот может хранить, это позволяет ему работать дольше без перезарядки. oc:tooltip.UpgradeChunkloader=Если робот движется в лесу и никто его не видит, действительно ли он движется? Это улучшение гарантирует, что движется. Оно держит чанк, на котором находится робот, загруженным, но постоянно потребляет энергию. diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua index 1131c3f7c..e89b2acd2 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/term.lua @@ -31,6 +31,16 @@ function term.clear() cursorX, cursorY = 1, 1 end +function term.reset() + if term.isAvailable() then + local maxw, maxh = component.gpu.maxResolution() + component.gpu.setResolution(maxw, maxh) + component.gpu.setBackground(0x000000) + component.gpu.setForeground(0xFFFFFF) + term.clear() + end +end + function term.clearLine() if term.isAvailable() then local w = component.gpu.getResolution() diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/usr/misc/greetings.txt b/src/main/resources/assets/opencomputers/loot/OpenOS/usr/misc/greetings.txt index 041959f85..68d49726b 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/usr/misc/greetings.txt +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/usr/misc/greetings.txt @@ -12,7 +12,7 @@ Most programs can be interrupted by pressing Ctrl+Alt+C. Paste the contents of the clipboard using the middle mouse button or a configurable key (default: insert). Computers will consume less power while idling - i.e. when os.sleep(n > 0.05) is called. Screens will consume more power the more lit characters they display. -Most blocks act as 'cables' - use switches and power distributers to create separate networks. +Most blocks act as 'cables' - use relays and power distributors to create separate networks. Welcome to the dark side - here, have some cookies. Screens can display Unicode - paste the special chars or use unicode.char. Run `help` or `man programname` for ingame help on programs shipped with OpenOS - start with `man man`. @@ -25,4 +25,4 @@ Have you tried turning it off and on again? To disable this greeting, install OpenOS to a writeable medium and delete `/etc/motd`. Did you know OpenComputers has a forum? No? Well, it's at http://oc.cil.li/. Please report bugs on the Github issue tracker, thank you! -Beware of cycles when building networks, or you may get duplicate messages! \ No newline at end of file +Beware of cycles when building networks, or you may get duplicate messages! diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua index b292f2291..d1fd62ba5 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua @@ -8,7 +8,7 @@ if #args == 0 then until not read else for i = 1, #args do - local file, reason = io.open(args[i]) + local file, reason = io.open(args[i],"rb")--TODO: make b an option if not file then io.stderr:write(reason .. "\n") return diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua index de4509434..afcf298c0 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua @@ -67,6 +67,8 @@ for n = 1, options.count do end if options.wait then os.sleep(tonumber(options.wait)) + else + os.sleep(0) end end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua index 9cbc74107..041514da9 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua @@ -5,7 +5,8 @@ local text = require("text") local args, options = shell.parse(...) local function formatSize(size) - if not options.h then + size = tonumber(size) or size + if not options.h or type(size) ~= "number" then return tostring(size) end local sizes = {"", "K", "M", "G"} diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua index f178f7eec..c1943cd7c 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua @@ -73,6 +73,7 @@ local code = "" codeHandler = function(char) if char == "[" then code = code .. char elseif char == "0" then code = code .. char + elseif char == "3" then code = code .. char elseif code == "[" and char == "A" then charHandler = baseHandler if y - 1 < 1 then return end @@ -139,6 +140,16 @@ codeHandler = function(char) out:write(text) out:close() end + elseif code == "[3" and char == "~" then + charHandler = baseHandler + if x > unicode.len(lines[y]) then + lines[y] = lines[y] .. (lines[y + 1] or "") + table.remove(lines, y + 1) + render(y, atline + edith - y) + return + end + lines[y] = lines[y]:sub(1, x-1) .. lines[y]:sub(x+1) + render(y, 1) else charHandler = baseHandler end @@ -149,7 +160,7 @@ baseHandler = function(char) code = "" charHandler = codeHandler elseif char == "\n" then - line = lines[y] + local line = lines[y] lines[y] = unicode.sub(line or "", 1, x - 1) table.insert(lines, y + 1, unicode.sub(line or "", x)) x = 1 diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua index 2f2a21992..9cedb28e2 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua @@ -199,6 +199,8 @@ local commandList = {} -- \x1b9[Row];[Col];[Height];[Width]F -- fill -- \x1b9[Row];[Col];[Height];[Width];[Dest Row];[Dest Col]c -- copy +--Add fake gpu component for compat(?) + function charHandlers.control(char) if char == "\x1b" then commandList = {} diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua index e7a9ba5cd..42a9f4862 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua @@ -2,6 +2,21 @@ local network = require "network" local computer = require "computer" local args = {...} +local function formatSize(size) + size = tonumber(size) or size + if type(size) ~= "number" then + return tostring(size) + end + local sizes = {"", "K", "M", "G"} + local unit = 1 + local power = 1024 + while size > power and unit < #sizes do + unit = unit + 1 + size = size / power + end + return math.floor(size * 10) / 10 .. sizes[unit] +end + local function align(txt)return txt .. (" "):sub(#txt+1)end if #args < 1 then @@ -14,7 +29,7 @@ if #args < 1 then local pktIn, pktOut, bytesIn, bytesOut = network.info.getInterfaceInfo(node) print(" RX packets:"..tostring(pktIn)) print(" TX packets:"..tostring(pktOut)) - print(" RX bytes:"..tostring(bytesIn).." TX bytes:"..tostring(bytesOut)) + print(" RX bytes: ".. tostring(bytesIn) .. " (" ..formatSize(bytesIn).. ") TX bytes: " ..tostring(bytesOut) .. " (".. formatSize(bytesOut) .. ")") end elseif args[1] == "bind" and args[2] then print("Address attached") diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua index 71146c458..02a8042b3 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua @@ -1,13 +1,28 @@ --Plan9k userspace init for pipes kernel + +--TODO: pcall all + emergency shell(or do it higher, in pipes) + local pipes = require("pipes") -local component = require("component") local filesystem = require("filesystem") +local component = require("component") os.setenv("LIBPATH", "/lib/?.lua;/usr/lib/?.lua;/home/lib/?.lua;./?.lua;/lib/?/init.lua;/usr/lib/?/init.lua;/home/lib/?/init.lua;./?/init.lua") os.setenv("PATH", "/usr/local/bin:/usr/bin:/bin:.") os.setenv("PWD", "/") os.setenv("PS1", "\x1b[33m$PWD\x1b[31m#\x1b[39m ") +pipes.log("INIT: Mounting filesystems") + +if filesystem.exists("/etc/fstab") then + for entry in io.lines("/etc/fstab") do + if entry:sub(1,1) ~= "#" then + + end + end +end + +pipes.log("INIT: Starting terminals") + if not filesystem.exists("/root") then filesystem.makeDirectory("/root") end @@ -37,7 +52,6 @@ end local sin, sout - local screens = component.list("screen") for gpu in component.list("gpu") do local screen = screens() @@ -85,7 +99,11 @@ end pcall(services) local kout = io.popen(function() - pipes.setThreadName("/bin/tee.lua") + if filesystem.exists("/kern.log") then + filesystem.remove("/kern.log.old") + filesystem.rename("/kern.log", "/kern.log.old") + end + pipes.setThreadName("[init]/logd") io.output(sout) loadfile("/bin/tee.lua", nil, _G)("/kern.log") end, "w") diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua index fe946e194..f00045057 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua @@ -1,5 +1,8 @@ local serialization = require("serialization") local term = require("term") +local fs = require("filesystem") + +local args = {...} local env = setmetatable({}, {__index = _ENV}) @@ -10,6 +13,13 @@ local function optrequire(...) end end +if args[1] and fs.exists(args[1]) then --non standard, require -i !!! + local f = io.open(args[1]) + local code = load(f:read("*all"), "="..args[1], "t", env) + f:close() + xpcall(code, debug.traceback) +end + local hist = {} while true do io.write(tostring(env._PROMPT or "lua> ")) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/more.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/more.lua new file mode 100644 index 000000000..648ef31c5 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/more.lua @@ -0,0 +1,44 @@ +local term = require("term") + +function usage() + print( +[[Usage: + more [options] ... + +A file perusal filter for CRT viewing.]]) +end + +local args = {...} +local file = args[1] +if not file then usage() return end + +file = io.open(file) + +if not file then + print("File not found") + return +end + +term.clear() +local _, h = term.getResolution() +io.write("\x1b[1;1H") +print("...",h) +for i = 1, h - 2 do + local line = file:read("*l") + if not line then print("input end")return end + print(line) +end + +io.write("\x1b47m\x1b30m--More--\x1b39m\x1b49m") + +while true do + local c = io.read(1) + if c == "\n" then + local line = file:read("*l") + if not line then return end + print("\r\x1b[K" .. line) + io.write("\x1b47m\x1b30m--More--\x1b39m\x1b49m") + elseif c == "q" then + return + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua index 5fb8c378c..db4c3b3ef 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua @@ -203,6 +203,7 @@ while run do if term.getCursor() > 1 then io.write("\n") end + io.write("\x1b49m\x1b39m") io.write(expand(os.getenv("PS1"))) local cmd = term.read(history)--io.read("*l") --print("--IN: ", cmd) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua index 828335ac3..41e324564 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua @@ -3,7 +3,7 @@ local args = {...} local f = io.open(args[1], "a") while true do - local data = io.read(1) + local data = io.read("*L") if not data then f:close() return @@ -11,7 +11,7 @@ while true do if io.input().remaining() > 0 then data = data .. io.read(io.input().remaining()) end + io.write(data) f:write(data) f:flush() - io.write(data) end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua index 806a414c6..61b03bd22 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua @@ -9,7 +9,6 @@ proxy.isReadOnly = function() return true end proxy.rename = function() error("Permission Denied") end proxy.remove = function() error("Permission Denied") end proxy.setLabel = function() error("Permission Denied") end -proxy.seek = function() error("Not supported") end proxy.size = function(path) local seg = kernel.modules.vfs.segments(path) local file = data @@ -22,7 +21,17 @@ proxy.getLabel = function() return "devfs" end local allocator, handles = kernel.modules.util.getAllocator() -proxy.exists = function()end +proxy.exists = function(path) + local seg = kernel.modules.vfs.segments(path) + local file = data + for _, d in pairs(seg) do + if not file[d] then + return false + end + file = file[d] + end + return file and true or false +end proxy.open = function(path) local seg = kernel.modules.vfs.segments(path) local file = data @@ -48,6 +57,9 @@ end proxy.write = function(h, ...) return handles[h].file.write(handles[h], ...) end +proxy.seek = function(h, ...) + return handles[h].file.seek(handles[h], ...) +end proxy.isDirectory = function(path) local seg = kernel.modules.vfs.segments(path) local dir = data @@ -69,8 +81,8 @@ proxy.list = function(path) error("File is not a directory") end local list = {} - for f in pairs(dir) do - list[#list + 1] = f + for f, node in pairs(dir) do + list[#list + 1] = f .. (node.__type and "" or "/") end return list end @@ -80,6 +92,12 @@ data.null = { __type = "f", write = function()end } +data.kmsg = { + __type = "f", + write = function(h, data) + kernel.io.println(data) + end +} data.zero = { __type = "f", read = function(h, c) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua index 7c60579b2..91a41d1a1 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua @@ -15,6 +15,7 @@ proxy.getLabel = function() return "procfs" end local allocator, handles = kernel.modules.util.getAllocator() proxy.exists = function()end + proxy.open = function(path) local seg = kernel.modules.vfs.segments(path) local file = data @@ -32,16 +33,21 @@ proxy.open = function(path) end return hnd.id end + proxy.read = function(h, n) return handles[h]:read(n) end + proxy.close = function(h) allocator:unset(handles[h]) end + proxy.write = function() error("Permission Denied") end + proxy.isDirectory = function() end + proxy.list = function(path) local seg = kernel.modules.vfs.segments(path) local dir = data @@ -51,11 +57,11 @@ proxy.list = function(path) local list = {} for pid, thr in pairs(kernel.modules.threading.threads) do if thr.coro and dir == data then - list[#list + 1] = tostring(pid) + list[#list + 1] = tostring(pid) .. "/" end end - for f in pairs(dir) do - list[#list + 1] = f + for f, node in pairs(dir) do + list[#list + 1] = f .. (type(node) == "table" and "/" or "") end return list end @@ -72,13 +78,36 @@ data.cpuinfo = function() "cpu family : 1\n" .. "model : 1\n" .. "model name : OpenComputers Lua CPU @ unkown Tier\n" .. - "microcode : 0x52\n" .. + "microcode : " .. (string.pack and "0x53" or "0x52") .. "\n" .. "physical id : 0\n" end +data.uptime = function() + return tostring(computer.uptime()) +end + setmetatable(data, {__index = function(_, k) if tonumber(k) and kernel.modules.threading.threads[tonumber(k)] and kernel.modules.threading.threads[tonumber(k)].coro then - return {comm = function()return kernel.modules.threading.threads[tonumber(k)].name end} + return { + comm = function()return kernel.modules.threading.threads[tonumber(k)].name end, + limits = function() + local limits = "Limit Units Soft Limit\n" + limits = limits .. "Max pending signals signals " .. kernel.modules.threading.threads[tonumber(k)].maxPendingSignals .. "\n" + limits = limits .. "Max open files files " .. kernel.modules.threading.threads[tonumber(k)].maxOpenFiles .. "\n" + return limits + end, + status = function() + local status = "" + status = status .. "Name: " .. kernel.modules.threading.threads[tonumber(k)].name .. "\n" + status = status .. "State: " .. coroutine.status(kernel.modules.threading.threads[tonumber(k)].coro) .. "\n" + status = status .. "Pid: " .. kernel.modules.threading.threads[tonumber(k)].pid .. "\n" + status = status .. "Uid: " .. kernel.modules.threading.threads[tonumber(k)].uid .. "\n" + + --TODO: count actual signals + status = status .. "SigQ: " .. #kernel.modules.threading.threads[tonumber(k)].eventQueue .. "/" .. kernel.modules.threading.threads[tonumber(k)].maxPendingSignals .. "\n" + return status + end + } end end}) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua index 5efd66a64..4be9c6c50 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua @@ -51,6 +51,7 @@ function kernel.userspace.os.spawn(prog, ...) local thread = kernel.modules.threading.spawn(prog, 0, name, isThread, _, ...) thread.io_output = kernel.modules.threading.currentThread.io_output thread.io_input = kernel.modules.threading.currentThread.io_input + thread.io_error = kernel.modules.threading.currentThread.io_error return thread.pid end @@ -78,6 +79,7 @@ end function kernel.userspace.os.exit() kernel.modules.threading.kill(kernel.modules.threading.currentThread.pid) + coroutine.yield("yield", 0) end function kernel.userspace.os.sleep(time) @@ -142,7 +144,12 @@ end kernel.userspace.coroutine = {} -kernel.userspace.coroutine.yield = function(...) - return coroutine.yield(...)--TODO: FIX; move to debug -end - +--lua 5.3 <-> 5.2 compat +kernel.userspace.bit32 = bit32 or load([[return { + band = function(a, b) return a & b end, + bor = function(a, b) return a | b end, + bxor = function(a, b) return a ~ b end, + bnot = function(a) return ~a end, + rshift = function(a, n) return a >> n end, + lshift = function(a, n) return a << n end, +}]])() diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua index bb72e1a5a..e25ba7113 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua @@ -39,18 +39,17 @@ end local function pathSearcher(module) local filepath, reason = kernel.userspace.package.searchpath(module, kernel.userspace.os.getenv("LIBPATH")) if filepath then - local loader, reason = kernel.userspace.loadfile(filepath, "bt", setmetatable({},{__index = kernel.userspace})) + local loader + loader, reason = kernel.userspace.loadfile(filepath, "bt", setmetatable({},{__index = kernel.userspace})) if loader then - local state, mod = pcall(loader) + local state + state, reason = pcall(loader) if state then - return mod - else - kernel.io.println("Module '" .. tostring(module) .. "' loading failed: " .. tostring(mod)) + return reason end end - else - return nil, reason end + return nil, reason end kernel.userspace.package.searchers[#kernel.userspace.package.searchers + 1] = preloadSearcher @@ -65,19 +64,23 @@ kernel.userspace.require = function(module) if kernel.userspace.package.loading[module] then error("Already loading "..tostring(module)) else + local reason + kernel.userspace.package.loading[module] = true for _, searcher in ipairs(kernel.userspace.package.searchers) do - local res, mod, reason = pcall(searcher, module) - if res and mod then + local success, mod, res = pcall(searcher, module) + if success and mod then kernel.userspace.package.loading[module] = nil kernel.userspace.package.loaded[module] = mod return mod - elseif not mod and reason then - kernel.io.println("Searcher for '" .. tostring(module) .. "' loading failed: " .. tostring(reason)) + elseif (not success) and mod then + reason = mod + elseif res then + reason = res end end kernel.userspace.package.loading[module] = nil - error("Could not load module " .. tostring(module)) + error(string.format("Could not load module '%s': %s", module, reason or "module returned nil")) end end end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_data.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_data.lua new file mode 100644 index 000000000..4676746f4 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_data.lua @@ -0,0 +1,47 @@ +cards = {} + + +local function buildDevfs() + for file in pairs(kernel.modules.devfs.data) do + if file == "urandom" then + kernel.modules.devfs.data[file] = nil + end + end + + kernel.modules.devfs.data["urandom"] = { + __type = "f", + read = function(h, len) + return component.invoke(cards[1], "random", len) + end + } +end + +local function onComponentAdded(_, address, componentType) + if componentType == "data" then + cards[#cards + 1] = address + buildDevfs() + end +end + +local function onComponentRemoved(_, address, componentType) + if componentType == "data" then + local t + for i, card in ipairs(cards) do + if card == address then + t = i + break + end + end + table.remove(cards, t) + buildDevfs() + end +end + +--function start() + --for card, t in component.list("data") do + -- onComponentAdded(_, card, t) + --end +--end + +kernel.modules.keventd.listen("component_added", onComponentAdded) +kernel.modules.keventd.listen("component_removed", onComponentRemoved) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_drive.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_drive.lua new file mode 100644 index 000000000..a080514d7 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_drive.lua @@ -0,0 +1,117 @@ +drives = {} + +function writeSectors(drive, data, at) + local sectorSize = component.invoke(drive, "getSectorSize") + repeat + local atSector = math.floor((at - 1) / sectorSize) + 1 + + local inSectorStart = (at - 1) % sectorSize + 1 + local writable = math.min(#data, sectorSize - inSectorStart + 1) + + local old = component.invoke(drive, "readSector", atSector) + + local before = old:sub(0, inSectorStart - 1) + local after = old:sub(inSectorStart + writable) + + local toWrite = before .. data:sub(1, writable) .. after + data = data:sub(writable + 1) + + kernel.io.println("Wd: " .. atSector .. "/" .. #toWrite .. ": "..inSectorStart.." [ " .. writable .. " ] "..(inSectorStart + writable) .. " #old="..#old) + component.invoke(drive, "writeSector", atSector, toWrite) + + at = at + writable + until #data < 1 +end + +function readSectors(drive, at, len) + local data = "" + local sectorSize = component.invoke(drive, "getSectorSize") + repeat + local atSector = math.floor(at / sectorSize) + 1 + + sector = component.invoke(drive, "readSector", atSector) + kernel.io.println("Rsect " .. atSector .. ": " .. tostring((at - 1) % sectorSize + 1) .. " -> " .. tostring(math.min((at - 1) % sectorSize + len - #data, sectorSize))) + local read = sector:sub((at - 1) % sectorSize + 1, math.min((at - 1) % sectorSize + len - #data, sectorSize)) + + data = data .. read + at = at + #read + until #data >= len + return data +end + +local function buildDevfs() + for file in pairs(kernel.modules.devfs.data) do + if file:match("^sd") then + kernel.modules.devfs.data[file] = nil + end + end + for k, drive in ipairs(drives) do + kernel.modules.devfs.data["sd" .. string.char(k + 96)] = { + __type = "f", + open = function(hnd) + --component.invoke(drive, "seek", -math.huge) + hnd.drive = drive + hnd.pos = 1 + --kernel.io.println("Od: " .. hnd.pos .. "/" .. component.invoke(drive, "getCapacity")) + end, + size = function() + return component.invoke(drive, "getCapacity") + end, + write = function(h, data) + + writeSectors(drive, data, h.pos) + --kernel.io.println("Wd: " .. h.pos .. "(+" .. #data .. ")/" .. component.invoke(drive, "getCapacity")) + h.pos = h.pos + #data + return not (h.pos >= component.invoke(drive, "getCapacity")) + --TODO: do this correctly + end, + read = function(h, len) + len = math.ceil(len) + kernel.io.println("Rd " .. tostring(len) .. ": " .. h.pos .. "/" .. component.invoke(drive, "getCapacity")) + if h.pos >= component.invoke(drive, "getCapacity") then + return + end + local data = readSectors(drive, h.pos, len) + h.pos = h.pos + len + return data + end, + seek = function(h, whence, offset) + offset = offset or 0 + if whence == "end" then + h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, component.invoke(drive, "getCapacity") - offset)) + return h.pos - 1 + elseif whence == "set" then + h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, 1 + offset)) + return h.pos - 1 + else + h.pos = math.min(component.invoke(drive, "getCapacity"), math.max(1, h.pos + offset)) + return h.pos - 1 + end + return math.floor(h.pos) + end + } + end +end + +local function onComponentAdded(_, address, componentType) + if componentType == "drive" then + drives[#drives + 1] = address + buildDevfs() + end +end + +local function onComponentRemoved(_, address, componentType) + if componentType == "drive" then + local t + for i, drive in ipairs(drives) do + if drive == address then + t = i + break + end + end + table.remove(drives, t) + buildDevfs() + end +end +kernel.modules.keventd.listen("component_added", onComponentAdded) +kernel.modules.keventd.listen("component_removed", onComponentRemoved) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua index 33ad4d5ff..84ba412ed 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua @@ -321,6 +321,7 @@ startNetwork = function() end sent = nil else + --we've already requested this addr so if we get the route --we'll respond routeRequests[dest][#routeRequests[dest]+1] = {type = "R", host = origin} diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua index faaf7efe9..359163dde 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua @@ -15,20 +15,33 @@ local function buildDevfs() end component.invoke(tape, "seek", -math.huge) hnd.tape = tape + hnd.pos = 0 end, size = function() return component.invoke(tape, "getSize") end, write = function(h, data) component.invoke(tape, "write", data) - return not component.invoke(tape, "isEnd", data) + h.pos = h.pos + #data + return not (h.pos >= component.invoke(tape, "getSize")) --TODO: do this correctly end, read = function(h, len) - if component.invoke(tape, "isEnd", data) then + if h.pos >= component.invoke(tape, "getSize") then return end + h.pos = h.pos + len return component.invoke(tape, "read", len) + end, + seek = function(h, whence, offset) + if whence == "end" then + h.pos = h.pos + component.invoke(tape, "seek", component.invoke(tape, "getSize") - h.pos - (offset or 0)) + elseif whence == "set" then + h.pos = h.pos + component.invoke(tape, "seek", (offset or 0) - h.pos) + else + h.pos = h.pos + component.invoke(tape, "seek", offset or 0) + end + return math.floor(h.pos) end } end @@ -55,11 +68,11 @@ local function onComponentRemoved(_, address, componentType) end end -function start() - for tape, t in component.list("tape_drive") do - onComponentAdded(_, tape, t) - end -end +--function start() +-- for tape, t in component.list("tape_drive") do +-- onComponentAdded(_, tape, t) +-- end +--end kernel.modules.keventd.listen("component_added", onComponentAdded) kernel.modules.keventd.listen("component_removed", onComponentRemoved) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua index 8dd4b8f2a..7d7015ed3 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua @@ -71,6 +71,8 @@ function spawn(exec, child, name, isthread, _, ...) currentHandlerArg = nil, eventQueue = {{"arg", ...}}, name = name or "unnamed", + maxPendingSignals = 32, + maxOpenFiles = 8, uid = nextUid } @@ -132,7 +134,7 @@ local function processSignals() for _, thread in ipairs(threads) do if thread.coro then local nsig, oldest = countThreadSignals(thread, "signal") - if nsig > 32 then --TODO: make it a bit more intelligent + if nsig > thread.maxPendingSignals then --TODO: make it a bit more intelligent table.remove(thread.eventQueue, oldest) end if thread.currentHandler == "yield" then diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua index d755e06ad..8c473878c 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua @@ -1,4 +1,5 @@ function joinThread(pid) + --coroutine.yield("yield", 0) while true do local dead = coroutine.yield("kill") if pid == dead then @@ -30,7 +31,7 @@ function userKill(pid, signal, ...) local args = {...} local thread = kernel.modules.threading.threads[pid] kernel.modules.manageg.protect(thread.sandbox) - --TODO: probably ser threading.currentThread here + --TODO: probably set threading.currentThread here local res, reason = pcall(function() thread.kill[signal](table.unpack(args)) end) @@ -41,7 +42,7 @@ function userKill(pid, signal, ...) return true end -function setKillHandler(signal, handler) +function setKillHandler(signal, handler) --WAT if not kernel.modules.threading.threads[pid] or not kernel.modules.threading.threads[pid].coro then return nil, "Thread does not exists" diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua index 21fda1bf1..5942d8322 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua @@ -1,6 +1,8 @@ local term = {} -local function read(from, to) +term.escape = "\x1b" +local write = function(text) io.write(term.escape..text) end +local read = function(from, to) local started, data while true do local char = io.read(1) @@ -19,6 +21,279 @@ local function read(from, to) end end end + +term.color={} +term.color.black=30 +term.color.red=31 +term.color.green=32 +term.color.yellow=33 +term.color.blue=34 +term.color.magenta=35 +term.color.cyan=36 +term.color.white=37 + +term.attr={} +term.attr.resetAllAttr=0 +term.attr.bright=1 +term.attr.dim=2 +term.attr.blink=5 +term.attr.reverse=7 +term.attr.hidden=8 + +------------------------------------------ +--Set Display Attributes +------------------------------------------ + +--Set Attribute Mode *ESC*[{attr1};...;{attrn}m +function term.setAttr(attr) + write(attr.."m") +end + +function term.setForeground(color) + write(color.."m") +end + +function term.setBackground(color) + write((color+10).."m") +end + +------------------------------------------ +--Erasing Text +------------------------------------------ + +--Erase End of Line *ESC*[K +--Erases from the current cursor position to the end of the current line. +function term.eraseEndOfLine() + write("[K") +end + +--Erase Start of Line *ESC*[1K +--Erases from the current cursor position to the start of the current line. +function term.eraseStartOfLine() + write("[1K") +end + +--Erase Line *ESC*[2K +--Erases the entire current line. +function term.eraseLine() + write("[2K") +end +term.clearLine = term.eraseLine + +--Erase Down *ESC*[J +--Erases the screen from the current line down to the bottom of the screen. +function term.eraseDown() + write("[J") +end + +--Erase Up *ESC*[1J +--Erases the screen from the current line up to the top of the screen. +function term.eraseUp() + write("[1J") +end + +--Erase Screen *ESC*[2J +--Erases the screen with the background colour and moves the cursor to home. +function term.clear() + write("[2J") +end + + +------------------------------------------- +--Tab Control +------------------------------------------ + +--Set Tab *ESC*H +--Sets a tab at the current position. +function term.tab() + write("[H") +end + +--Clear Tab *ESC*[g +--Clears tab at the current position. +function term.clearTab() + write("[g") +end + +--Clear All Tabs *ESC*[3g +--Clears all tabs. +function term.clearTabs() + write("[3g") +end + + +------------------------------------------ +--Scrolling +------------------------------------------ + +--Scroll Screen *ESC*[r +--Enable scrolling for entire display. +function term.enableScroll() + write("[r") +end + +--Scroll Screen *ESC*[{start};{end}r +--Enable scrolling from row {start} to row {end}. +function term.scrollScreen(from,to) + write(string.format("[%d;%dr",from,to)) +end + +--Scroll Down *ESC*D +--Scroll display down one line. +function term.scrollScreenDown() + write("D") +end + +--Scroll Up *ESC*M +--Scroll display up one line. +function term.scrollScreenUp() + write("M") +end + + +------------------------------------------ +--Cursor Control +------------------------------------------ + +--Cursor Home *ESC*[{ROW};{COLUMN}H +--Sets the cursor position where subsequent text will begin. If no row/column parameters are provided (ie. *ESC*[H), the cursor will move to the home position, at the upper left of the screen. +function term.setCursorPosition(row,col) + write(string.format("[%d;%dH", row, col)) +end + +function term.resetCursor() + write("[H") +end + +--Cursor Up *ESC*[{COUNT}A +--Moves the cursor up by COUNT rows; the default count is 1. +function term.cursorUp(count) + write(string.format("[%dA",(count or 1))) +end + +--Cursor Down *ESC*[{COUNT}B +--Moves the cursor down by COUNT rows; the default count is 1. +function term.cursorDown(count) + write(string.format("[%dB",(count or 1))) +end + +--Cursor Forward *ESC*[{COUNT}C +--Moves the cursor forward by COUNT columns; the default count is 1. +function term.cursorForward(count) + write(string.format("[%dC",(count or 1))) +end + +--Cursor Backward *ESC*[{COUNT}D +--Moves the cursor backward by COUNT columns; the default count is 1. +function term.cursorBackward(count) + write(string.format("[%dD",(count or 1))) +end + +--Force Cursor Position *ESC*[{ROW};{COLUMN}f +--Identical to Cursor Home. +function term.forceCursorPosition(row, col) + write(string.format("[%d;%df", row, col)) +end + +--Save Cursor *ESC*[s +--Save current cursor position. +function term.saveCursor() + write("[s") +end + +--Unsave Cursor *ESC*[u +--Restores cursor position after a Save Cursor. +function term.restoreCursor() + write("[u") +end + +--Save Cursor & Attrs *ESC*7 +--Save current cursor position. +function term.saveCursorAndAttr() + write("7") +end + +--Restore Cursor & Attrs *ESC*8 +--Restores cursor position after a Save Cursor. +function term.restoreCursorAndAttr() + write("8") +end + + +------------------------------------------ +--Terminal Setup +------------------------------------------ + +--Reset Device *ESC*c +--Reset all terminal settings to default. +function term.reset() + write("c") +end + +--Enable Line Wrap *ESC*[7h +--Text wraps to next line if longer than the length of the display area. +function term.enableLineWrap() + write("[7h") +end + +--Disable Line Wrap *ESC*[7l +--Disables line wrapping. +function term.disableLineWrap() + write("[7l") +end + +------------------------------------------ +--Plan9k codes +------------------------------------------ + +-- \x1b9[H];[W]R - set resolution +function term.setResolution(height,width) + write(string.format("9%d;%dR", height, width)) +end + +-- \x1b9[Row];[Col];[Height];[Width]F -- fill +function term.fill(row, col, height, width) + write(string.format("9%d;%d;%d;%dF", row, col, height, width)) +end + +-- \x1b9[Row];[Col];[Height];[Width];[Dest Row];[Dest Col]c -- copy +function term.copy(row, col, height, width, destRow, destCol) + write(string.format("9%d;%d;%d;%d;%d;%dc", row, col, height, width, destRow, destCol )) +end + +--get resolution +function term.getResolution() + local y, x = term.getCursorPosition() + term.setCursorPosition(999,999) + local h, w = term.getCursorPosition() + term.setCursorPosition(y,x) + return tonumber(h), tonumber(w) +end + + + + + + + + + + + + + + + + + + + + + + + + + function term.clear() io.write("\x1b[2J") diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/base64.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/base64.lua new file mode 100644 index 000000000..2a5d9d58f --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/base64.lua @@ -0,0 +1,42 @@ +local shell = require("shell") +local data = require("data") + + +local args, parms = shell.parse(...) +if parms.h or parms.help then + io.stderr:write("See: man base64" .. "\n") + os.exit(true) +end +local encodingfun = nil +local encode +if parms.d or parms.decode then + encodingfun = data.decode64 + encode = false +else + encodingfun = data.encode64 + encode = true +end + +if #args == 0 then + repeat + local read = io.read(encode and 3 or 4) + if read then + io.write(encodingfun(read)) + end + until not read +else + for i = 1, #args do + local file, reason = io.open(shell.resolve(args[i])) + if not file then + io.stderr:write(tostring(reason) .. "\n") + os.exit(false) + end + repeat + local line = file:read(encode and 3 or 4) + if line then + io.write(encodingfun(line)) + end + until not line + file:close() + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/deflate.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/deflate.lua new file mode 100644 index 000000000..cd8660ac2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/deflate.lua @@ -0,0 +1,25 @@ +local shell = require("shell") +local data = require("data") + +local args = shell.parse(...) +if #args == 0 then + local read = "" + repeat + local current = io.read("*a") + read = read .. current + until current ~= "" + io.write(data.deflate(read)) +else + local read = "" + local file, reason = io.open(shell.resolve(args[1])) + if not file then + io.stderr:write(tostring(reason) .. "\n") + os.exit(false) + end + repeat + local current = file:read("*a") + read = read .. current + until current ~= "" + file:close() + io.write(data.deflate(read)) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/gpg.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/gpg.lua new file mode 100644 index 000000000..5b2f5ad41 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/gpg.lua @@ -0,0 +1,244 @@ +--[[ +-- A program that allows user to perform all crypto operations provided by Tier II / Tier III data cards +-- Author: makkarpov +--]] + +local shell = require("shell") +local data = require("data") +local term = require("term") +local filesystem = require("filesystem") +local serialization = require("serialization") + +local args, options = shell.parse(...) + +local function writeFile(path, data) + if filesystem.exists(path) then + io.stderr:write("gpg: failed to write file: " .. path .. "\n") + io.stderr:write("gpg: error was: file already exists\n") + return false + end + + if type(data) == "table" then + data = serialization.serialize(data) + end + + local h, err = io.open(path, "wb") + + if not h then + io.stderr:write("gpg: failed to write file: " .. path .. "\n") + io.stderr:write("gpg: error was: " .. err .. "\n") + return false + end + + h:write(data) + h:close() + return true +end + +local function readFile(path, deserialize) + local h = io.open(path, "rb") + local r = h:read("*a") + h:close() + + if deserialize then + r = serialization.unserialize(r) + end + + return r +end + +local function parseKey(path, isPublic) + local d = readFile(path, true) + local k, err = data.deserializeKey(d.d, d.t) + + if not k then + io.stderr:write("gpg: failed to parse key: " .. err .. "\n") + return nil + end + + if k.isPublic() ~= isPublic then + io.stderr:write("gpg: wrong key type\n") + return nil + end + + return k +end + +local function deriveName(base, encrypt) + if encrypt then + return base .. ".gpg" + else + local d = base:gsub(".gpg", "") + if d == base then + d = d .. ".dec" + io.write("gpg: decrypting to " .. d .. "\n") + end + return d + end +end + +local function ensureMethods(...) + if not require("component").isAvailable("data") then + io.stderr:write("gpg: you must have data card in order to run this program\n") + error("data card is absent") + end + + local names = table.pack(...) + for i = 1, names.n do + if names[i] and not data[names[i]] then + io.stderr:write("gpg: method " .. names[i] .. " required on data card to run this program\n") + error("data card tier insufficient") + end + end +end + +if options['g'] and (#args == 2) then + ensureMethods("generateKeyPair") + local pub, priv = data.generateKeyPair(384) + + priv = { t = priv.keyType(), d = priv.serialize() } + pub = { t = pub.keyType(), d = pub.serialize() } + + if not writeFile(args[1], priv) then + io.stderr:write("gpg: failed to write private key, aborting\n") + return false + end + + if not writeFile(args[2], pub) then + io.stderr:write("gpg: failed to write public key, aborting\n") + return false + end + + return true +end + +if options['c'] and (options['e'] or options['d']) and (#args == 1) then + ensureMethods("md5", "sha256", "encrypt", "decrypt", "random") + if options['d'] and options['e'] then + io.stderr:write("gpg: please specify either -d or -e\n") + return false + end + + io.write("gpg: enter password: ") + local aesKey = data.md5(term.read(nil, nil, nil, "*")) + local checkValue = data.sha256(aesKey) + + if options['e'] then + local iv = data.random(16) + local d = data.encrypt(readFile(args[1]), aesKey, iv) + + return writeFile(deriveName(args[1], true), { + t = "pwd", + kdf = "md5", + iv = iv, + cv = checkValue, + d = d + }) + else + local d = readFile(args[1], true) + + if d.t ~= "pwd" then + io.stderr:write("gpg: file is not encrypted with a password\n") + return false + end + + if checkValue ~= d.cv then + io.stderr:write("gpg: password incorrect\n") + return false + end + + return writeFile(deriveName(args[1], false), data.decrypt(d.d, aesKey, d.iv)) + end +end + +if (options['d'] or options['e']) and (#args == 2) then + ensureMethods("md5", "sha256", "encrypt", "decrypt", "random", "generateKeyPair", "deserializeKey", "ecdh") + if options['d'] and options['e'] then + io.stderr:write("gpg: please specify either -d or -e\n") + return false + end + + if options['e'] then + local userPub = parseKey(args[1], true) + local tmpPub, tmpPriv = data.generateKeyPair(384) + local aesKey = data.md5(data.ecdh(tmpPriv, userPub)) + local checkValue = data.sha256(aesKey) + local iv = data.random(16) + + local d = data.encrypt(readFile(args[2]), aesKey, iv) + return writeFile(deriveName(args[2], true), { + t = "ecdh", + kdf = "md5", + iv = iv, + cv = checkValue, + k = { + t = tmpPub.keyType(), + d = tmpPub.serialize() + }, + d = d + }) + else + local userPriv = parseKey(args[1], false) + local d = readFile(args[2], true) + + if d.t ~= "ecdh" then + io.stderr:write("gpg: file is not encrypted with a key\n") + return false + end + + local tmpPub = data.deserializeKey(d.k.d, d.k.t) + local aesKey = data.md5(data.ecdh(userPriv, tmpPub)) + + if d.cv ~= data.sha256(aesKey) then + io.stderr:write("gpg: invalid key\n") + return false + end + + return writeFile(deriveName(args[2], false), data.decrypt(d.d, aesKey, d.iv)) + end +end + +if (options['s'] or options['v']) and (#args == 2) then + ensureMethods("deserializeKey", "ecdsa") + if options['s'] and options['v'] then + io.stderr:write("gpg: please specify either -s or -v\n") + return false + end + + if options['s'] then + local userPriv = parseKey(args[1], false) + local sign = data.ecdsa(readFile(args[2]), userPriv) + + return writeFile(args[2] .. ".sig", { + t = "ecdsa", + s = sign + }) + else + local userPub = parseKey(args[1], true) + local sign = readFile(args[2] .. ".sig", true) + + if sign.t ~= "ecdsa" then + io.stderr:write("gpg: unsupported signature type\n") + return false + end + + if not data.ecdsa(readFile(args[2]), userPub, sign.s) then + io.stderr:write("gpg: signature verification failed\n") + return false + end + + io.write("gpg: signature is valid\n") + + return true + end +end + +io.write("Usages:\n") +io.write("gpg -ce -- encrypt file with password\n") +io.write("gpg -cd -- decrypt file with password\n") +io.write("gpg -e -- encrypt file\n") +io.write("gpg -d -- decrypt file\n") +io.write("gpg -g -- generate keypair\n") +io.write("gpg -s -- sign file\n") +io.write("gpg -v -- verify file\n") +return false diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/inflate.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/inflate.lua new file mode 100644 index 000000000..349c5f18b --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/inflate.lua @@ -0,0 +1,25 @@ +local shell = require("shell") +local data = require("data") + +local args = shell.parse(...) +if #args == 0 then + local read = "" + repeat + local current = io.read("*a") + read = read .. current + until current ~= "" + io.write(data.inflate(read)) +else + local read = "" + local file, reason = io.open(shell.resolve(args[1])) + if not file then + io.stderr:write(tostring(reason) .. "\n") + os.exit(false) + end + repeat + local current = file:read("*a") + read = read .. current + until current ~= "" + file:close() + io.write(data.inflate(read)) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/md5sum.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/md5sum.lua new file mode 100644 index 000000000..37cba695f --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/md5sum.lua @@ -0,0 +1,27 @@ +local shell = require("shell") +local data = require("data") + +local args = shell.parse(...) +if #args == 0 then + local read = "" + repeat + local current = io.read("*a") + read = read .. current + until current ~= "" + io.write(data.toHex(data.md5(read))) +else + for i = 1, #args do + local read = "" + local file, reason = io.open(args[i]) + if not file then + io.stderr:write(tostring(reason) .. "\n") + os.exit(false) + end + repeat + local current = file:read("*a") + read = read .. current + until current ~= "" + file:close() + io.write(data.toHex(data.md5(read)) .. "\t".. args[i] .. "\n") + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua index 5f9ef04de..49cdb030e 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua @@ -81,17 +81,25 @@ ocBackend = { print(" Upgrade all packages that are out-of-date on the") print(" local system. Only package versions are used to find outdated packages;") print(" replacements are not checked here. This is a ?remove-then-add? process.") + print(" -f, --force") + print(" Force operation, in case of upgrade it redownloads all packages") print(" --root='/some/dir'") print(" Set alternative root directory") print(" -v") print(" More output") print(" -y") print(" Don't ask any questions, answer automatically") + print(" -r, --reboot") + print(" reboot after operation") return end if options.v then loglevel = 0 end + if options.f or options.force then + core.data.force = true + end + if options.S or options.sync then for _, pack in ipairs(args) do core.install(pack) @@ -114,6 +122,8 @@ ocBackend = { if options.y then ocBackend.prompt = function()return true end end + + core.reboot = optionsr or options.reboot core.doWork() end, @@ -208,7 +218,8 @@ ocBackend = { if p:sub(1,1):upper() ~= "Y" then error("User stopped") end - end + end, + reboot = function()computer.shutdown(true) end } local mptFrontend @@ -249,7 +260,7 @@ mptFrontend = { local toCheck = {} for pack, data in pairs(base.installed) do if data.frontend == mptFrontend.name then - toCheck[pack] = base.installed[pack].data.checksum + toCheck[pack] = base.installed[pack].data.checksum .. (core.data.force and "WAT" or "") end end local updateResp = backend.getText(config.frontend.mpt.api.."update", toCheck) @@ -368,6 +379,8 @@ core = { upgrade = false, remove = false, + force = false, + --User requested packages userInstall = {}, @@ -603,7 +616,8 @@ end core.log(1, "Main", "> Saving settings") core.finalize() - - +if core.reboot then + backend.reboot() +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/sha256sum.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/sha256sum.lua new file mode 100644 index 000000000..33d5b25c7 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/sha256sum.lua @@ -0,0 +1,27 @@ +local shell = require("shell") +local data = require("data") + +local args = shell.parse(...) +if #args == 0 then + local read = "" + repeat + local current = io.read("*a") + read = read .. current + until current ~= "" + io.write(data.toHex(data.sha256(read))) +else + for i = 1, #args do + local read = "" + local file, reason = io.open(shell.resolve(args[i])) + if not file then + io.stderr:write(tostring(reason) .. "\n") + os.exit(false) + end + repeat + local current = file:read("*a") + read = read .. current + until current ~= "" + file:close() + io.write(data.toHex(data.sha256(read)) .. "\t".. args[i] .. "\n") + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/lib/data.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/lib/data.lua new file mode 100644 index 000000000..04fa9ff9c --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/lib/data.lua @@ -0,0 +1,26 @@ +local component = require("component") + +local data = {} + +------------------------------------------------------------------------------- + +-- Converts binary data into hexadecimal string. +function data.toHex(data) + return (data:gsub('.', function (c) + return string.format('%02X', string.byte(c)) + end)) +end + +-- Converts hexadecimal string into binary data. +function data.fromHex(hex) + return (hex:gsub('..', function (cc) + return string.char(tonumber(cc, 16)) + end)) +end + +-- Forward everything else to the primary data card. +setmetatable(data, { __index = function(_, key) return component.data[key] end }) + +------------------------------------------------------------------------------- + +return data diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db index 92ac3946c..3ecc57b30 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db @@ -1 +1 @@ -{cacheDir="/var/lib/mpt/cache/",database="/var/lib/mpt/base.db",frontend={mpt={api="http://mpt.magik6k.net/api/"}}} \ No newline at end of file +{database="/var/lib/mpt/base.db",cacheDir="/var/lib/mpt/cache/",frontend={mpt={api="http://mpt.magik6k.net/api/"}}} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db index 4c7665a18..d64373528 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db @@ -1 +1 @@ -{installed={["plan9k-drivers"]={frontend="MPT",data={name="plan9k-drivers",repo="plan9k",checksum="-2327b14292d5c08f547d24e9b5122349",files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua","/lib/modules/base/17_nfc.lua","/lib/modules/base/17_chatbox.lua"},dependencies={}},deps={}},["plan9k-shell"]={frontend="MPT",data={name="plan9k-shell",repo="plan9k",checksum="-4ebb18f6dedde575d4d61460494b3bbb",files={"/bin/sh.lua"},dependencies={}},deps={}},["plan9k-corelibs"]={frontend="MPT",data={name="plan9k-corelibs",repo="plan9k",checksum="1d01d437b10e8561a39cb9f108cc3d47",files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},dependencies={}},deps={}},mpt={frontend="MPT",data={name="mpt",repo="mpt",checksum="f9d7744571e5c46c658f405043c656",files={"/usr/bin/mpt.lua"},dependencies={}},deps={}},["plan9k-fsutil"]={frontend="MPT",data={name="plan9k-fsutil",repo="plan9k",checksum="aded7c8083efcdcfe43908183370687",files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua","/bin/touch.lua","/bin/mount.lua","/bin/mount.cow.lua","/bin/mkdir.lua","/bin/pwd.lua"},dependencies={"plan9k-corelibs"}},deps={"plan9k-corelibs"}},["plan9k-network"]={frontend="MPT",data={name="plan9k-network",repo="plan9k",checksum="-29d10c88b7ed4116d5b4eacd019d5ad9",files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},dependencies={}},deps={}},pipes={frontend="MPT",data={name="pipes",repo="plan9k",checksum="6d088970fd4fb29b78279eef4d417646",files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua","/lib/modules/base/06_cowfs.lua","/lib/modules/base/09_rootfs.lua","/lib/modules/base/01_gc.lua"},dependencies={"openloader-init"}},deps={"openloader-init"}},["plan9k-edit"]={frontend="MPT",data={name="plan9k-edit",repo="plan9k",checksum="fed5f4ee1212297b07247afa1cfe3a2",files={"/bin/edit.lua"},dependencies={}},deps={}},["plan9k-core"]={frontend="MPT",data={name="plan9k-core",repo="plan9k",checksum="-4f5e4f44875482035444a2b61e96243d",files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua"},dependencies={"pipes","plan9k-coreutil","plan9k-shell"}},deps={"pipes","plan9k-coreutil","plan9k-shell"}},plan9k={frontend="MPT",data={name="plan9k",repo="plan9k",checksum="-c16ccdf21a1ff13be8f6258d1a17d89",files={},dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"}},deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"}},["plan9k-installer"]={frontend="MPT",data={name="plan9k-installer",repo="plan9k",checksum="52c8f82357c966ce3e19c97bf3942012",files={"/bin/install.lua"},dependencies={"plan9k","mpt"}},deps={"plan9k","mpt"}},["openloader-init"]={frontend="MPT",data={name="openloader-init",repo="disks",checksum="-45e6d7b1e41468c1d335952ee3b89e13",files={"/init.lua"},dependencies={}},deps={}},["plan9k-coreutil"]={frontend="MPT",data={name="plan9k-coreutil",repo="plan9k",checksum="-50d3cee571b07b6cb05f6fb01989997c",files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua","/bin/shutdown.lua","/bin/label.lua","/bin/uptime.lua","/bin/resolution.lua"},dependencies={"plan9k-corelibs","plan9k-fsutil"}},deps={"plan9k-corelibs","plan9k-fsutil"}}}} \ No newline at end of file +{installed={["plan9k-shell"]={deps={},frontend="MPT",data={checksum="517b9e0693e734a4ab516f12a6798f14",files={"/bin/sh.lua"},name="plan9k-shell",repo="plan9k",dependencies={}}},plan9k={deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit","plan9k-data"},frontend="MPT",data={checksum="-2d8f4b84ea60b0c9d5846f57e9f1691c",files={},name="plan9k",repo="plan9k",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit","plan9k-data"}}},["plan9k-fsutil"]={deps={"plan9k-corelibs"},frontend="MPT",data={checksum="6a974a71f62315e6e2294eae132d1751",files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua","/bin/touch.lua","/bin/mount.lua","/bin/mount.cow.lua","/bin/mkdir.lua","/bin/pwd.lua","/bin/more.lua"},name="plan9k-fsutil",repo="plan9k",dependencies={"plan9k-corelibs"}}},["plan9k-edit"]={deps={},frontend="MPT",data={checksum="34b1046ac9b7a87a1cdd74f8c03f27ea",files={"/bin/edit.lua"},name="plan9k-edit",repo="plan9k",dependencies={}}},["plan9k-core"]={deps={"pipes","plan9k-coreutil","plan9k-shell"},frontend="MPT",data={checksum="-6f77a020200f96eefd1559dcd8af14a5",files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua"},name="plan9k-core",repo="plan9k",dependencies={"pipes","plan9k-coreutil","plan9k-shell"}}},["plan9k-installer"]={deps={"plan9k","mpt"},frontend="MPT",data={checksum="52c8f82357c966ce3e19c97bf3942012",files={"/bin/install.lua"},name="plan9k-installer",repo="plan9k",dependencies={"plan9k","mpt"}}},["plan9k-drivers"]={deps={},frontend="MPT",data={checksum="-35f098652460458c13f83499b4633e2d",files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua","/lib/modules/base/17_nfc.lua","/lib/modules/base/17_chatbox.lua","/lib/modules/base/17_data.lua","/lib/modules/base/17_drive.lua"},name="plan9k-drivers",repo="plan9k",dependencies={}}},["plan9k-coreutil"]={deps={"plan9k-corelibs","plan9k-fsutil"},frontend="MPT",data={checksum="5fa7e70e3aba17fef97ed65489efb0ca",files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua","/bin/shutdown.lua","/bin/label.lua","/bin/uptime.lua","/bin/resolution.lua"},name="plan9k-coreutil",repo="plan9k",dependencies={"plan9k-corelibs","plan9k-fsutil"}}},["openloader-init"]={deps={},frontend="MPT",data={checksum="-45e6d7b1e41468c1d335952ee3b89e13",files={"/init.lua"},name="openloader-init",repo="disks",dependencies={}}},mpt={deps={},frontend="MPT",data={checksum="-3435ded79cf9de20e7185f403eee4a28",files={"/usr/bin/mpt.lua"},name="mpt",repo="mpt",dependencies={}}},pipes={deps={"openloader-init"},frontend="MPT",data={checksum="123b56992859cf05cd03af825437d734",files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua","/lib/modules/base/06_cowfs.lua","/lib/modules/base/09_rootfs.lua","/lib/modules/base/01_gc.lua"},name="pipes",repo="plan9k",dependencies={"openloader-init"}}},["plan9k-corelibs"]={deps={},frontend="MPT",data={checksum="1cb7fa12e1a1a35fc168409b60cfe552",files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},name="plan9k-corelibs",repo="plan9k",dependencies={}}},["plan9k-data"]={deps={},frontend="MPT",data={checksum="480a898a741b2bf424f9e0e86e5072ba",files={"/usr/bin/base64.lua","/usr/lib/data.lua","/usr/bin/deflate.lua","/usr/bin/inflate.lua","/usr/bin/md5sum.lua","/usr/bin/sha256sum.lua","/usr/bin/gpg.lua"},name="plan9k-data",repo="plan9k",dependencies={}}},["plan9k-network"]={deps={},frontend="MPT",data={checksum="6e06e6cf5028583fe54047ba9e19abc6",files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},name="plan9k-network",repo="plan9k",dependencies={}}}}} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lua/machine.lua b/src/main/resources/assets/opencomputers/lua/machine.lua index 56e79e3c0..238fd4f51 100644 --- a/src/main/resources/assets/opencomputers/lua/machine.lua +++ b/src/main/resources/assets/opencomputers/lua/machine.lua @@ -1320,6 +1320,23 @@ local libcomputer = { beep = function(...) libcomponent.invoke(computer.address(), "beep", ...) + end, + + getArchitectures = function(...) + return spcall(computer.getArchitectures, ...) + end, + getArchitecture = function(...) + return spcall(computer.getArchitecture, ...) + end, + setArchitecture = function(...) + local result, reason = spcall(computer.setArchitecture, ...) + if not result then + if reason then + return result, reason + end + else + coroutine.yield(true) -- reboot + end end } sandbox.computer = libcomputer diff --git a/src/main/resources/assets/opencomputers/models/block/relay.json b/src/main/resources/assets/opencomputers/models/block/relay.json new file mode 100644 index 000000000..0af961954 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/block/relay.json @@ -0,0 +1,8 @@ +{ + "parent": "block/cube_bottom_top", + "textures": { + "top": "opencomputers:blocks/switch_top", + "bottom": "opencomputers:blocks/generic_top", + "side": "opencomputers:blocks/switch_side" + } +} diff --git a/src/main/resources/assets/opencomputers/models/block/transposer.json b/src/main/resources/assets/opencomputers/models/block/transposer.json new file mode 100644 index 000000000..5e20eb017 --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/block/transposer.json @@ -0,0 +1,221 @@ +{ + "textures": { + "bottom": "opencomputers:blocks/transposer_top", + "top": "opencomputers:blocks/transposer_top", + "side": "opencomputers:blocks/transposer_side", + "particle": "opencomputers:blocks/generic_top" + }, + "elements": [ + { "from": [ 0, 0, 0 ], + "to": [ 7, 7, 7 ], + "faces": { + "up": { "uv": [ 0, 0, 7, 7 ], "texture": "#top" }, + "down": { "uv": [ 0, 9, 7, 16 ], "texture": "#bottom" }, + "north": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" }, + "south": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" }, + "west": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" }, + "east": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" } + } + }, + { "from": [ 0, 0, 9 ], + "to": [ 7, 7, 16 ], + "faces": { + "up": { "uv": [ 0, 9, 7, 16 ], "texture": "#top" }, + "down": { "uv": [ 0, 0, 7, 7 ], "texture": "#bottom" }, + "north": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" }, + "south": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" }, + "west": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" }, + "east": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" } + } + }, + { "from": [ 0, 9, 0 ], + "to": [ 7, 16, 7 ], + "faces": { + "up": { "uv": [ 0, 0, 7, 7 ], "texture": "#top" }, + "down": { "uv": [ 0, 9, 7, 16 ], "texture": "#bottom" }, + "north": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" }, + "south": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" }, + "west": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" }, + "east": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" } + } + }, + { "from": [ 0, 9, 9 ], + "to": [ 7, 16, 16 ], + "faces": { + "up": { "uv": [ 0, 9, 7, 16 ], "texture": "#top" }, + "down": { "uv": [ 0, 0, 7, 7 ], "texture": "#bottom" }, + "north": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" }, + "south": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" }, + "west": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" }, + "east": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" } + } + }, + { "from": [ 9, 0, 0 ], + "to": [ 16, 7, 7 ], + "faces": { + "up": { "uv": [ 9, 0, 16, 7 ], "texture": "#top" }, + "down": { "uv": [ 9, 9, 16, 16 ], "texture": "#bottom" }, + "north": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" }, + "south": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" }, + "west": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" }, + "east": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" } + } + }, + { "from": [ 9, 0, 9 ], + "to": [ 16, 7, 16 ], + "faces": { + "up": { "uv": [ 9, 9, 16, 16 ], "texture": "#top" }, + "down": { "uv": [ 9, 0, 16, 7 ], "texture": "#bottom" }, + "north": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" }, + "south": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" }, + "west": { "uv": [ 9, 9, 16, 16 ], "texture": "#side" }, + "east": { "uv": [ 0, 9, 7, 16 ], "texture": "#side" } + } + }, + { "from": [ 9, 9, 0 ], + "to": [ 16, 16, 7 ], + "faces": { + "up": { "uv": [ 9, 0, 16, 7 ], "texture": "#top" }, + "down": { "uv": [ 9, 9, 16, 16 ], "texture": "#bottom" }, + "north": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" }, + "south": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" }, + "west": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" }, + "east": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" } + } + }, + { "from": [ 9, 9, 9 ], + "to": [ 16, 16, 16 ], + "faces": { + "up": { "uv": [ 9, 9, 16, 16 ], "texture": "#top" }, + "down": { "uv": [ 9, 0, 16, 7 ], "texture": "#bottom" }, + "north": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" }, + "south": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" }, + "west": { "uv": [ 9, 0, 16, 7 ], "texture": "#side" }, + "east": { "uv": [ 0, 0, 7, 7 ], "texture": "#side" } + } + }, + + { "from": [ 0, 0, 7 ], + "to": [ 5, 5, 9 ], + "faces": { + "up": { "uv": [ 0, 7, 5, 9 ], "texture": "#top" }, + "down": { "uv": [ 0, 7, 5, 9 ], "texture": "#bottom" }, + "west": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" }, + "east": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" } + } + }, + { "from": [ 0, 11, 7 ], + "to": [ 5, 16, 9 ], + "faces": { + "up": { "uv": [ 0, 7, 5, 9 ], "texture": "#top" }, + "down": { "uv": [ 0, 7, 5, 9 ], "texture": "#bottom" }, + "west": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" }, + "east": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" } + } + }, + { "from": [ 11, 0, 7 ], + "to": [ 16, 5, 9 ], + "faces": { + "up": { "uv": [ 11, 7, 16, 9 ], "texture": "#top" }, + "down": { "uv": [ 11, 7, 16, 9 ], "texture": "#bottom" }, + "west": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" }, + "east": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" } + } + }, + { "from": [ 11, 11, 7 ], + "to": [ 16, 16, 9 ], + "faces": { + "up": { "uv": [ 11, 7, 16, 9 ], "texture": "#top" }, + "down": { "uv": [ 11, 7, 16, 9 ], "texture": "#bottom" }, + "west": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" }, + "east": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" } + } + }, + + { "from": [ 0, 7, 0 ], + "to": [ 5, 9, 5 ], + "faces": { + "north": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" }, + "south": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" }, + "west": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" }, + "east": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" } + } + }, + { "from": [ 0, 7, 11 ], + "to": [ 5, 9, 16 ], + "faces": { + "north": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" }, + "south": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" }, + "west": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" }, + "east": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" } + } + }, + { "from": [ 11, 7, 0 ], + "to": [ 16, 9, 5 ], + "faces": { + "north": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" }, + "south": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" }, + "west": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" }, + "east": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" } + } + }, + { "from": [ 11, 7, 11 ], + "to": [ 16, 9, 16 ], + "faces": { + "north": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" }, + "south": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" }, + "west": { "uv": [ 11, 7, 16, 9 ], "texture": "#side" }, + "east": { "uv": [ 0, 7, 5, 9 ], "texture": "#side" } + } + }, + + { "from": [ 7, 0, 0 ], + "to": [ 9, 5, 5 ], + "faces": { + "up": { "uv": [ 7, 0, 9, 5 ], "texture": "#top" }, + "down": { "uv": [ 7, 11, 9, 16 ], "texture": "#bottom" }, + "north": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" }, + "south": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" } + } + }, + { "from": [ 7, 0, 11 ], + "to": [ 9, 5, 16 ], + "faces": { + "up": { "uv": [ 7, 11, 9, 16 ], "texture": "#top" }, + "down": { "uv": [ 7, 0, 9, 5 ], "texture": "#bottom" }, + "north": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" }, + "south": { "uv": [ 7, 11, 9, 16 ], "texture": "#side" } + } + }, + { "from": [ 7, 11, 0 ], + "to": [ 9, 16, 5 ], + "faces": { + "up": { "uv": [ 7, 0, 9, 5 ], "texture": "#top" }, + "down": { "uv": [ 7, 11, 9, 16 ], "texture": "#bottom" }, + "north": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" }, + "south": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" } + } + }, + { "from": [ 7, 11, 11 ], + "to": [ 9, 16, 16 ], + "faces": { + "up": { "uv": [ 7, 11, 9, 16 ], "texture": "#top" }, + "down": { "uv": [ 7, 0, 9, 5 ], "texture": "#bottom" }, + "north": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" }, + "south": { "uv": [ 7, 0, 9, 5 ], "texture": "#side" } + } + }, + + { "from": [ 1, 1, 1 ], + "to": [ 15, 15, 15 ], + "faces": { + "up": { "uv": [ 1, 1, 15, 15 ], "texture": "#top" }, + "down": { "uv": [ 1, 1, 15, 15 ], "texture": "#bottom" }, + "north": { "uv": [ 1, 1, 15, 15 ], "texture": "#side" }, + "south": { "uv": [ 1, 1, 15, 15 ], "texture": "#side" }, + "west": { "uv": [ 1, 1, 15, 15 ], "texture": "#side" }, + "east": { "uv": [ 1, 1, 15, 15 ], "texture": "#side" } + } + } + ] +} diff --git a/src/main/resources/assets/opencomputers/models/item/relay.json b/src/main/resources/assets/opencomputers/models/item/relay.json new file mode 100644 index 000000000..9283695ae --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/relay.json @@ -0,0 +1,10 @@ +{ + "parent": "opencomputers:block/relay", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/opencomputers/models/item/transposer.json b/src/main/resources/assets/opencomputers/models/item/transposer.json new file mode 100644 index 000000000..843975aad --- /dev/null +++ b/src/main/resources/assets/opencomputers/models/item/transposer.json @@ -0,0 +1,10 @@ +{ + "parent": "opencomputers:block/transposer", + "display": { + "thirdperson": { + "rotation": [ 10, -45, 170 ], + "translation": [ 0, 1.5, -2.75 ], + "scale": [ 0.375, 0.375, 0.375 ] + } + } +} diff --git a/src/main/resources/assets/opencomputers/recipes/default.recipes b/src/main/resources/assets/opencomputers/recipes/default.recipes index 456f87c33..9bb0d1f2d 100644 --- a/src/main/resources/assets/opencomputers/recipes/default.recipes +++ b/src/main/resources/assets/opencomputers/recipes/default.recipes @@ -504,11 +504,6 @@ interweb { [string, string, string]] } -accessPoint { - input: [[ingotIron, "oc:wlanCard", ingotIron] - ["oc:cable", "oc:lanCard", "oc:cable"] - [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] -} adapter { input: [[ingotIron, "oc:cable", ingotIron] ["oc:cable", "oc:circuitChip1", "oc:cable"] @@ -607,7 +602,7 @@ powerDistributor { serverRack { input: [["oc:circuitChip2", "oc:wlanCard", "oc:circuitChip2"] [fenceIron, chest, fenceIron] - ["oc:switch", "oc:materialCircuitBoardPrinted", "oc:powerDistributor"]] + ["oc:relay", "oc:materialCircuitBoardPrinted", "oc:powerDistributor"]] } raid { input: [[nuggetIron, "oc:cpu3", nuggetIron] @@ -619,7 +614,7 @@ redstone { [blockRedstone, "oc:redstoneCard1", blockRedstone] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] } -switch { +relay { input: [[ingotIron, "oc:cable", ingotIron] ["oc:cable", "oc:lanCard", "oc:cable"] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] @@ -639,6 +634,12 @@ screen3 { [yellowDust, "oc:circuitChip3", glass] [obsidian, yellowDust, obsidian]] } +transposer { + input: [[ingotIron, "oc:inventoryControllerUpgrade", ingotIron] + [hopper, bucket, hopper] + [ingotIron, "oc:tankControllerUpgrade", ingotIron]] + output: 4 +} waypoint { input: [[ingotIron, "oc:circuitChip1", ingotIron] ["oc:materialTransistor", "oc:materialInterweb", "oc:materialTransistor"], diff --git a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes b/src/main/resources/assets/opencomputers/recipes/gregtech.recipes index 4c95c8eb8..f2b3768c7 100644 --- a/src/main/resources/assets/opencomputers/recipes/gregtech.recipes +++ b/src/main/resources/assets/opencomputers/recipes/gregtech.recipes @@ -318,7 +318,7 @@ powerDistributor { serverRack { input: [[craftingToolScrewdriver, "oc:wlanCard", craftingToolWrench] ["ic2.reactorVentDiamond", chest, "ic2.reactorVentDiamond"] - ["oc:switch", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] + ["oc:relay", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] } redstone { # 32731 = Activity Detector @@ -326,7 +326,7 @@ redstone { [{item="gt.metaitem.01", subID=32731}, {block="gt.blockcasings", subID=2}, "oc:redstoneCard1"] ["oc:circuitChip2", "oc:materialCircuitBoardPrinted", "oc:circuitChip2"]] } -switch { +relay { input: [["", "oc:lanCard", ""] ["oc:cable", {block="gt.blockcasings", subID=2}, "oc:cable"] ["oc:materialCircuitBoardPrinted", craftingToolWrench, "oc:materialCircuitBoardPrinted"]] diff --git a/src/main/resources/assets/opencomputers/recipes/hardmode.recipes b/src/main/resources/assets/opencomputers/recipes/hardmode.recipes index ed930d4f3..fe0614746 100644 --- a/src/main/resources/assets/opencomputers/recipes/hardmode.recipes +++ b/src/main/resources/assets/opencomputers/recipes/hardmode.recipes @@ -363,14 +363,14 @@ powerDistributor { serverRack { input: [["oc:circuitChip3", "oc:wlanCard", "oc:circuitChip3"] [fenceIron, chest, fenceIron] - ["oc:switch", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] + ["oc:relay", "oc:materialCircuitBoardPrinted","oc:powerDistributor"]] } redstone { input: [[ingotIron, "oc:circuitChip3", ingotIron] [blockRedstone, "oc:redstoneCard1", blockRedstone] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] } -switch { +relay { input: [[ingotIron, "oc:cable", ingotIron] ["oc:cable", "oc:lanCard", "oc:cable"] [ingotIron, "oc:materialCircuitBoardPrinted", ingotIron]] diff --git a/src/main/resources/assets/opencomputers/textures/blocks/Transposer_top.png b/src/main/resources/assets/opencomputers/textures/blocks/Transposer_top.png new file mode 100644 index 000000000..63f2b8fd0 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/Transposer_top.png differ diff --git a/src/main/resources/assets/opencomputers/textures/blocks/overlay/transposer_on.png b/src/main/resources/assets/opencomputers/textures/blocks/overlay/transposer_on.png new file mode 100644 index 000000000..0f2a818a7 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/overlay/transposer_on.png differ diff --git a/src/main/resources/assets/opencomputers/textures/blocks/transposer_side.png b/src/main/resources/assets/opencomputers/textures/blocks/transposer_side.png new file mode 100644 index 000000000..8d10906de Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/blocks/transposer_side.png differ diff --git a/src/main/resources/assets/opencomputers/textures/gui/upgrade_tab.png b/src/main/resources/assets/opencomputers/textures/gui/upgrade_tab.png new file mode 100644 index 000000000..2ca3b14c7 Binary files /dev/null and b/src/main/resources/assets/opencomputers/textures/gui/upgrade_tab.png differ diff --git a/src/main/scala/li/cil/oc/Constants.scala b/src/main/scala/li/cil/oc/Constants.scala index addb367c6..42cbe9eec 100644 --- a/src/main/scala/li/cil/oc/Constants.scala +++ b/src/main/scala/li/cil/oc/Constants.scala @@ -25,12 +25,14 @@ object Constants { final val Keyboard = "keyboard" final val Microcontroller = "microcontroller" final val MotionSensor = "motionSensor" + final val NetSplitter = "netSplitter" final val PowerConverter = "powerConverter" final val PowerDistributor = "powerDistributor" final val Print = "print" final val Printer = "printer" final val Raid = "raid" final val Redstone = "redstone" + final val Relay = "relay" final val Robot = "robot" final val RobotAfterimage = "robotAfterimage" final val ScreenTier1 = "screen1" @@ -38,7 +40,7 @@ object Constants { final val ScreenTier3 = "screen3" final val ServerRack = "serverRack" final val Switch = "switch" - final val NetSplitter = "netSplitter" + final val Transposer = "transposer" final val Waypoint = "waypoint" def Case(tier: Int) = ItemUtils.caseNameWithTierSuffix("case", tier) diff --git a/src/main/scala/li/cil/oc/Settings.scala b/src/main/scala/li/cil/oc/Settings.scala index e4db25e40..aa8138f1c 100644 --- a/src/main/scala/li/cil/oc/Settings.scala +++ b/src/main/scala/li/cil/oc/Settings.scala @@ -201,6 +201,7 @@ class Settings(val config: Config) { val dataCardComplex = config.getDouble("power.cost.dataCardComplex") max 0 val dataCardComplexByte = config.getDouble("power.cost.dataCardComplexByte") max 0 val dataCardAsymmetric = config.getDouble("power.cost.dataCardAsymmetric") max 0 + val transposerCost = config.getDouble("power.cost.transposer") max 0 // power.rate val accessPointRate = config.getDouble("power.rate.accessPoint") max 0 @@ -336,6 +337,7 @@ class Settings(val config: Config) { val dataCardHardLimit = config.getInt("misc.dataCardHardLimit") max 0 val dataCardTimeout = config.getDouble("misc.dataCardTimeout") max 0 val serverRackSwitchTier = (config.getInt("misc.serverRackSwitchTier") - 1) max Tier.None min Tier.Three + val redstoneDelay = config.getDouble("misc.redstoneDelay") max 0 // ----------------------------------------------------------------------- // // printer diff --git a/src/main/scala/li/cil/oc/client/GuiHandler.scala b/src/main/scala/li/cil/oc/client/GuiHandler.scala index 48d5ea80a..a886e372e 100644 --- a/src/main/scala/li/cil/oc/client/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/client/GuiHandler.scala @@ -36,12 +36,14 @@ object GuiHandler extends CommonGuiHandler { new gui.DiskDrive(player.inventory, t) case t: tileentity.Printer if id == GuiType.Printer.id => new gui.Printer(player.inventory, t) - case t: tileentity.Raid if id == GuiType.Raid.id => - new gui.Raid(player.inventory, t) - case t: tileentity.RobotProxy if id == GuiType.Robot.id => - new gui.Robot(player.inventory, t.robot) case t: tileentity.ServerRack if id == GuiType.Rack.id => new gui.ServerRack(player.inventory, t) + case t: tileentity.Raid if id == GuiType.Raid.id => + new gui.Raid(player.inventory, t) + case t: tileentity.Relay if id == GuiType.Relay.id => + new gui.Relay(player.inventory, t) + case t: tileentity.RobotProxy if id == GuiType.Robot.id => + new gui.Robot(player.inventory, t.robot) case t: tileentity.Screen if id == GuiType.Screen.id => new gui.Screen(t.origin.buffer, t.tier > 0, () => t.origin.hasKeyboard, () => t.origin.buffer.isRenderingEnabled) case t: tileentity.Switch if id == GuiType.Switch.id => diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index bb4cb64d5..9a2ea387d 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -81,6 +81,7 @@ object PacketHandler extends CommonPacketHandler { case PacketType.ServerPresence => onServerPresence(p) case PacketType.Sound => onSound(p) case PacketType.SoundPattern => onSoundPattern(p) + case PacketType.TransposerActivity => onTransposerActivity(p) case PacketType.WaypointLabel => onWaypointLabel(p) case _ => // Invalid packet. } @@ -260,6 +261,15 @@ object PacketHandler extends CommonPacketHandler { } } + def onNetSplitterState(p: PacketParser) = + p.readTileEntity[NetSplitter]() match { + case Some(t) => + t.isInverted = p.readBoolean() + t.openSides = t.uncompressSides(p.readByte()) + t.world.markBlockForUpdate(t.getPos) + case _ => // Invalid packet. + } + def onParticleEffect(p: PacketParser) = { val dimension = p.readInt() world(p.player, dimension) match { @@ -420,7 +430,7 @@ object PacketHandler extends CommonPacketHandler { } def onSwitchActivity(p: PacketParser) = - p.readTileEntity[Switch]() match { + p.readTileEntity[traits.SwitchLike]() match { case Some(t) => t.lastMessage = System.currentTimeMillis() case _ => // Invalid packet. } @@ -589,15 +599,6 @@ object PacketHandler extends CommonPacketHandler { buffer.rawSetForeground(col, row, color) } - def onNetSplitterState(p: PacketParser) = - p.readTileEntity[NetSplitter]() match { - case Some(t) => - t.isInverted = p.readBoolean() - t.openSides = t.uncompressSides(p.readByte()) - t.world.markBlockForUpdate(t.getPos) - case _ => // Invalid packet. - } - def onScreenTouchMode(p: PacketParser) = p.readTileEntity[Screen]() match { case Some(t) => t.invertTouchMode = p.readBoolean() @@ -638,6 +639,12 @@ object PacketHandler extends CommonPacketHandler { } } + def onTransposerActivity(p: PacketParser) = + p.readTileEntity[Transposer]() match { + case Some(transposer) => transposer.lastOperation = System.currentTimeMillis() + case _ => // Invalid packet. + } + def onWaypointLabel(p: PacketParser) = p.readTileEntity[Waypoint]() match { case Some(waypoint) => waypoint.label = p.readUTF() diff --git a/src/main/scala/li/cil/oc/client/Proxy.scala b/src/main/scala/li/cil/oc/client/Proxy.scala index 6f33385aa..e799b35bc 100644 --- a/src/main/scala/li/cil/oc/client/Proxy.scala +++ b/src/main/scala/li/cil/oc/client/Proxy.scala @@ -63,15 +63,17 @@ private[oc] class Proxy extends CommonProxy { else ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Hologram], HologramRendererFallback) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Microcontroller], MicrocontrollerRenderer) + ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.NetSplitter], NetSplitterRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.PowerDistributor], PowerDistributorRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Printer], PrinterRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Raid], RaidRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.ServerRack], ServerRackRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Switch], SwitchRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.AccessPoint], SwitchRenderer) + ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Relay], SwitchRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.RobotProxy], RobotRenderer) ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Screen], ScreenRenderer) - ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.NetSplitter], NetSplitterRenderer) + ClientRegistry.bindTileEntitySpecialRenderer(classOf[tileentity.Transposer], TransposerRenderer) ClientRegistry.registerKeyBinding(KeyBindings.materialCosts) ClientRegistry.registerKeyBinding(KeyBindings.clipboardPaste) diff --git a/src/main/scala/li/cil/oc/client/Textures.scala b/src/main/scala/li/cil/oc/client/Textures.scala index 39505d661..e01630ba0 100644 --- a/src/main/scala/li/cil/oc/client/Textures.scala +++ b/src/main/scala/li/cil/oc/client/Textures.scala @@ -59,6 +59,7 @@ object Textures { val RobotSelection = L("robot_selection") val Server = L("server") val Slot = L("slot") + val UpgradeTab = L("upgrade_tab") val Waypoint = L("waypoint") override protected def basePath = "textures/gui/%s.png" @@ -142,6 +143,7 @@ object Textures { val RaidFrontError = L("overlay/raid_front_error") val ScreenUpIndicator = L("overlay/screen_up_indicator") val SwitchSideOn = L("overlay/switch_side_on") + val TransposerOn = L("overlay/transposer_on") val Cable = L("cable") val CableCap = L("cableCap") diff --git a/src/main/scala/li/cil/oc/client/gui/Relay.scala b/src/main/scala/li/cil/oc/client/gui/Relay.scala new file mode 100644 index 000000000..c8cb3432b --- /dev/null +++ b/src/main/scala/li/cil/oc/client/gui/Relay.scala @@ -0,0 +1,123 @@ +package li.cil.oc.client.gui + +import java.lang.Iterable +import java.text.DecimalFormat +import java.util + +import codechicken.nei.VisiblityData +import codechicken.nei.api.INEIGuiHandler +import codechicken.nei.api.TaggedInventoryArea +import li.cil.oc.Localization +import li.cil.oc.client.Textures +import li.cil.oc.common.container +import li.cil.oc.common.tileentity +import li.cil.oc.integration.Mods +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.client.renderer.Tessellator +import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.item.ItemStack +import net.minecraftforge.fml.common.Optional +import org.lwjgl.opengl.GL11 +import org.lwjgl.util.Rectangle + +@Optional.Interface(iface = "codechicken.nei.api.INEIGuiHandler", modid = Mods.IDs.NotEnoughItems) +class Relay(playerInventory: InventoryPlayer, val relay: tileentity.Relay) extends DynamicGuiContainer(new container.Relay(playerInventory, relay)) with INEIGuiHandler { + private val format = new DecimalFormat("#.##hz") + + private val tabPosition = new Rectangle(xSize, 10, 23, 26) + + override protected def drawSecondaryBackgroundLayer(): Unit = { + super.drawSecondaryBackgroundLayer() + + // Tab background. + GL11.glColor4f(1, 1, 1, 1) + Minecraft.getMinecraft.getTextureManager.bindTexture(Textures.GUI.UpgradeTab) + val x = windowX + tabPosition.getX + val y = windowY + tabPosition.getY + val w = tabPosition.getWidth + val h = tabPosition.getHeight + val t = Tessellator.getInstance + val r = t.getWorldRenderer + r.startDrawingQuads() + r.addVertexWithUV(x, y + h, zLevel, 0, 1) + r.addVertexWithUV(x + w, y + h, zLevel, 1, 1) + r.addVertexWithUV(x + w, y, zLevel, 1, 0) + r.addVertexWithUV(x, y, zLevel, 0, 0) + t.draw() + } + + override def mouseClicked(mouseX: Int, mouseY: Int, button: Int): Unit = { + // So MC doesn't throw away the item in the upgrade slot when we're trying to pick it up... + val originalWidth = xSize + try { + xSize += tabPosition.getWidth + super.mouseClicked(mouseX, mouseY, button) + } + finally { + xSize = originalWidth + } + } + + override def mouseReleased(mouseX: Int, mouseY: Int, button: Int): Unit = { + // So MC doesn't throw away the item in the upgrade slot when we're trying to pick it up... + val originalWidth = xSize + try { + xSize += tabPosition.getWidth + super.mouseReleased(mouseX, mouseY, button) + } + finally { + xSize = originalWidth + } + } + + override def drawSecondaryForegroundLayer(mouseX: Int, mouseY: Int) = { + super.drawSecondaryForegroundLayer(mouseX, mouseY) + fontRendererObj.drawString( + Localization.localizeImmediately(relay.getName), + 8, 6, 0x404040) + + fontRendererObj.drawString( + Localization.Switch.TransferRate, + 14, 20, 0x404040) + fontRendererObj.drawString( + Localization.Switch.PacketsPerCycle, + 14, 39, 0x404040) + fontRendererObj.drawString( + Localization.Switch.QueueSize, + 14, 58, 0x404040) + + fontRendererObj.drawString( + format.format(20f / inventoryContainer.relayDelay), + 108, 20, 0x404040) + fontRendererObj.drawString( + inventoryContainer.packetsPerCycleAvg + " / " + inventoryContainer.relayAmount, + 108, 39, thresholdBasedColor(inventoryContainer.packetsPerCycleAvg, math.ceil(inventoryContainer.relayAmount / 2f).toInt, inventoryContainer.relayAmount)) + fontRendererObj.drawString( + inventoryContainer.queueSize + " / " + inventoryContainer.maxQueueSize, + 108, 58, thresholdBasedColor(inventoryContainer.queueSize, inventoryContainer.maxQueueSize / 2, inventoryContainer.maxQueueSize)) + } + + private def thresholdBasedColor(value: Int, yellow: Int, red: Int) = { + if (value < yellow) 0x009900 + else if (value < red) 0x999900 + else 0x990000 + } + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def modifyVisiblity(gui: GuiContainer, currentVisibility: VisiblityData): VisiblityData = null + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def getItemSpawnSlots(gui: GuiContainer, stack: ItemStack): Iterable[Integer] = null + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def getInventoryAreas(gui: GuiContainer): util.List[TaggedInventoryArea] = null + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def handleDragNDrop(gui: GuiContainer, mouseX: Int, mouseY: Int, stack: ItemStack, button: Int): Boolean = false + + @Optional.Method(modid = Mods.IDs.NotEnoughItems) + override def hideItemPanelSlot(gui: GuiContainer, x: Int, y: Int, w: Int, h: Int): Boolean = { + new Rectangle(x - windowX, y - windowY, w, h).intersects(tabPosition) + } +} diff --git a/src/main/scala/li/cil/oc/client/gui/Switch.scala b/src/main/scala/li/cil/oc/client/gui/Switch.scala index aefe609ed..22096def3 100644 --- a/src/main/scala/li/cil/oc/client/gui/Switch.scala +++ b/src/main/scala/li/cil/oc/client/gui/Switch.scala @@ -7,6 +7,7 @@ import li.cil.oc.common.container import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer +// TODO Remove in 1.7 class Switch(playerInventory: InventoryPlayer, val switch: tileentity.Switch) extends DynamicGuiContainer(new container.Switch(playerInventory, switch)) { private val format = new DecimalFormat("#.##hz") diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala index 7041bcf2c..2849a780a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/SwitchRenderer.scala @@ -12,7 +12,7 @@ object SwitchRenderer extends TileEntitySpecialRenderer { override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float, damage: Int) { RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)") - val switch = tileEntity.asInstanceOf[tileentity.Switch] + val switch = tileEntity.asInstanceOf[tileentity.traits.SwitchLike] val activity = math.max(0, 1 - (System.currentTimeMillis() - switch.lastMessage) / 1000.0) if (activity > 0) { RenderState.pushAttrib() diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala new file mode 100644 index 000000000..f922d64c1 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/TransposerRenderer.scala @@ -0,0 +1,78 @@ +package li.cil.oc.client.renderer.tileentity + +import li.cil.oc.client.Textures +import li.cil.oc.common.tileentity +import li.cil.oc.util.RenderState +import net.minecraft.client.renderer.Tessellator +import net.minecraft.client.renderer.texture.TextureMap +import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer +import net.minecraft.tileentity.TileEntity +import org.lwjgl.opengl.GL11 + +object TransposerRenderer extends TileEntitySpecialRenderer { + override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float, damage: Int) { + RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)") + + val transposer = tileEntity.asInstanceOf[tileentity.Transposer] + val activity = math.max(0, 1 - (System.currentTimeMillis() - transposer.lastOperation) / 1000.0) + if (activity > 0) { + RenderState.pushAttrib() + + RenderState.disableEntityLighting() + RenderState.makeItBlend() + RenderState.setBlendAlpha(activity.toFloat) + + RenderState.pushMatrix() + + GL11.glTranslated(x + 0.5, y + 0.5, z + 0.5) + GL11.glScaled(1.0025, -1.0025, 1.0025) + GL11.glTranslatef(-0.5f, -0.5f, -0.5f) + + val t = Tessellator.getInstance + val r = t.getWorldRenderer + + Textures.Block.bind() + r.startDrawingQuads() + + val icon = Textures.getSprite(Textures.Block.TransposerOn) + r.addVertexWithUV(0, 1, 0, icon.getMaxU, icon.getMinV) + r.addVertexWithUV(1, 1, 0, icon.getMinU, icon.getMinV) + r.addVertexWithUV(1, 1, 1, icon.getMinU, icon.getMaxV) + r.addVertexWithUV(0, 1, 1, icon.getMaxU, icon.getMaxV) + + r.addVertexWithUV(0, 0, 0, icon.getMaxU, icon.getMaxV) + r.addVertexWithUV(0, 0, 1, icon.getMaxU, icon.getMinV) + r.addVertexWithUV(1, 0, 1, icon.getMinU, icon.getMinV) + r.addVertexWithUV(1, 0, 0, icon.getMinU, icon.getMaxV) + + r.addVertexWithUV(1, 1, 0, icon.getMinU, icon.getMaxV) + r.addVertexWithUV(0, 1, 0, icon.getMaxU, icon.getMaxV) + r.addVertexWithUV(0, 0, 0, icon.getMaxU, icon.getMinV) + r.addVertexWithUV(1, 0, 0, icon.getMinU, icon.getMinV) + + r.addVertexWithUV(0, 1, 1, icon.getMinU, icon.getMaxV) + r.addVertexWithUV(1, 1, 1, icon.getMaxU, icon.getMaxV) + r.addVertexWithUV(1, 0, 1, icon.getMaxU, icon.getMinV) + r.addVertexWithUV(0, 0, 1, icon.getMinU, icon.getMinV) + + r.addVertexWithUV(0, 1, 0, icon.getMinU, icon.getMaxV) + r.addVertexWithUV(0, 1, 1, icon.getMaxU, icon.getMaxV) + r.addVertexWithUV(0, 0, 1, icon.getMaxU, icon.getMinV) + r.addVertexWithUV(0, 0, 0, icon.getMinU, icon.getMinV) + + r.addVertexWithUV(1, 1, 1, icon.getMinU, icon.getMaxV) + r.addVertexWithUV(1, 1, 0, icon.getMaxU, icon.getMaxV) + r.addVertexWithUV(1, 0, 0, icon.getMaxU, icon.getMinV) + r.addVertexWithUV(1, 0, 1, icon.getMinU, icon.getMinV) + + t.draw() + + RenderState.enableEntityLighting() + + RenderState.popMatrix() + RenderState.popAttrib() + } + + RenderState.checkError(getClass.getName + ".renderTileEntityAt: leaving") + } +} diff --git a/src/main/scala/li/cil/oc/common/GuiHandler.scala b/src/main/scala/li/cil/oc/common/GuiHandler.scala index a0a7aaa5d..7c80029f9 100644 --- a/src/main/scala/li/cil/oc/common/GuiHandler.scala +++ b/src/main/scala/li/cil/oc/common/GuiHandler.scala @@ -30,6 +30,8 @@ abstract class GuiHandler extends IGuiHandler { new container.Printer(player.inventory, t) case t: tileentity.Raid if id == GuiType.Raid.id => new container.Raid(player.inventory, t) + case t: tileentity.Relay if id == GuiType.Relay.id => + new container.Relay(player.inventory, t) case t: tileentity.RobotProxy if id == GuiType.Robot.id => new container.Robot(player.inventory, t.robot) case t: tileentity.ServerRack if id == GuiType.Rack.id => diff --git a/src/main/scala/li/cil/oc/common/GuiType.scala b/src/main/scala/li/cil/oc/common/GuiType.scala index 11a6dbb5b..bf02db237 100644 --- a/src/main/scala/li/cil/oc/common/GuiType.scala +++ b/src/main/scala/li/cil/oc/common/GuiType.scala @@ -28,6 +28,7 @@ object GuiType extends ScalaEnum { val Printer = new EnumVal { def name = "Printer"; def subType = GuiType.Category.Block } val Rack = new EnumVal { def name = "Rack"; def subType = GuiType.Category.Block } val Raid = new EnumVal { def name = "Raid"; def subType = GuiType.Category.Block } + val Relay = new EnumVal { def name = "Relay"; def subType = GuiType.Category.Block } val Robot = new EnumVal { def name = "Robot"; def subType = GuiType.Category.Block } val Screen = new EnumVal { def name = "Screen"; def subType = GuiType.Category.Block } val Server = new EnumVal { def name = "Server"; def subType = GuiType.Category.Item } diff --git a/src/main/scala/li/cil/oc/common/InventorySlots.scala b/src/main/scala/li/cil/oc/common/InventorySlots.scala index 6961b5bd4..1ac7ef962 100644 --- a/src/main/scala/li/cil/oc/common/InventorySlots.scala +++ b/src/main/scala/li/cil/oc/common/InventorySlots.scala @@ -120,6 +120,13 @@ object InventorySlots { ) ) + val relay = Array( + InventorySlot(Slot.CPU, Tier.Three), + InventorySlot(Slot.Memory, Tier.Three), + InventorySlot(Slot.HDD, Tier.Three), + InventorySlot(Slot.Card, Tier.Three) + ) + val switch = Array( InventorySlot(Slot.CPU, Tier.Three), InventorySlot(Slot.Memory, Tier.Three), diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala index 0b3712d29..325612a99 100644 --- a/src/main/scala/li/cil/oc/common/PacketType.scala +++ b/src/main/scala/li/cil/oc/common/PacketType.scala @@ -20,6 +20,7 @@ object PacketType extends Enumeration { HologramTranslation, HologramValues, LootDisk, + NetSplitterState, ParticleEffect, PetVisibility, // Goes both ways. PowerState, @@ -49,11 +50,11 @@ object PacketType extends Enumeration { TextBufferMultiRawSetBackground, TextBufferMultiRawSetForeground, TextBufferPowerChange, - NetSplitterState, ScreenTouchMode, ServerPresence, Sound, SoundPattern, + TransposerActivity, WaypointLabel, // Goes both ways. // Client -> Server diff --git a/src/main/scala/li/cil/oc/common/block/AccessPoint.scala b/src/main/scala/li/cil/oc/common/block/AccessPoint.scala index be8320dff..463d21341 100644 --- a/src/main/scala/li/cil/oc/common/block/AccessPoint.scala +++ b/src/main/scala/li/cil/oc/common/block/AccessPoint.scala @@ -4,6 +4,7 @@ import li.cil.oc.Settings import li.cil.oc.common.tileentity import net.minecraft.world.World +// TODO Remove in 1.7 class AccessPoint extends Switch with traits.PowerAcceptor { override def energyThroughput = Settings.get.accessPointRate diff --git a/src/main/scala/li/cil/oc/common/block/Relay.scala b/src/main/scala/li/cil/oc/common/block/Relay.scala new file mode 100644 index 000000000..8f1356566 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/Relay.scala @@ -0,0 +1,17 @@ +package li.cil.oc.common.block + +import li.cil.oc.Settings +import li.cil.oc.common.GuiType +import li.cil.oc.common.tileentity +import net.minecraft.block.state.IBlockState +import net.minecraft.world.World + +class Relay extends SimpleBlock with traits.GUI with traits.PowerAcceptor { + override def guiType = GuiType.Relay + + override def energyThroughput = Settings.get.accessPointRate + + override def hasTileEntity(state: IBlockState) = true + + override def createNewTileEntity(world: World, metadata: Int) = new tileentity.Relay() +} diff --git a/src/main/scala/li/cil/oc/common/block/Switch.scala b/src/main/scala/li/cil/oc/common/block/Switch.scala index ab8c82e1d..f61016f14 100644 --- a/src/main/scala/li/cil/oc/common/block/Switch.scala +++ b/src/main/scala/li/cil/oc/common/block/Switch.scala @@ -5,6 +5,7 @@ import li.cil.oc.common.tileentity import net.minecraft.block.state.IBlockState import net.minecraft.world.World +// TODO Remove in 1.7 class Switch extends SimpleBlock with traits.GUI { override def guiType = GuiType.Switch diff --git a/src/main/scala/li/cil/oc/common/block/Transposer.scala b/src/main/scala/li/cil/oc/common/block/Transposer.scala new file mode 100644 index 000000000..ecd43afc2 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/block/Transposer.scala @@ -0,0 +1,18 @@ +package li.cil.oc.common.block + +import li.cil.oc.common.tileentity +import net.minecraft.block.state.IBlockState +import net.minecraft.util.BlockPos +import net.minecraft.util.EnumFacing +import net.minecraft.world.IBlockAccess +import net.minecraft.world.World + +class Transposer extends SimpleBlock { + override def isSideSolid(world: IBlockAccess, pos: BlockPos, side: EnumFacing): Boolean = false + + // ----------------------------------------------------------------------- // + + override def hasTileEntity(state: IBlockState): Boolean = true + + override def createNewTileEntity(world: World, meta: Int) = new tileentity.Transposer() +} diff --git a/src/main/scala/li/cil/oc/common/container/Relay.scala b/src/main/scala/li/cil/oc/common/container/Relay.scala new file mode 100644 index 000000000..d9ee4a54f --- /dev/null +++ b/src/main/scala/li/cil/oc/common/container/Relay.scala @@ -0,0 +1,33 @@ +package li.cil.oc.common.container + +import li.cil.oc.common.Slot +import li.cil.oc.common.tileentity +import net.minecraft.entity.player.InventoryPlayer +import net.minecraft.nbt.NBTTagCompound + +class Relay(playerInventory: InventoryPlayer, relay: tileentity.Relay) extends Player(playerInventory, relay) { + addSlotToContainer(151, 15, Slot.CPU) + addSlotToContainer(151, 34, Slot.Memory) + addSlotToContainer(151, 53, Slot.HDD) + addSlotToContainer(178, 15, Slot.Card) + addPlayerInventorySlots(8, 84) + + def relayDelay = synchronizedData.getInteger("relayDelay") + + def relayAmount = synchronizedData.getInteger("relayAmount") + + def maxQueueSize = synchronizedData.getInteger("maxQueueSize") + + def packetsPerCycleAvg = synchronizedData.getInteger("packetsPerCycleAvg") + + def queueSize = synchronizedData.getInteger("queueSize") + + override protected def detectCustomDataChanges(nbt: NBTTagCompound): Unit = { + synchronizedData.setInteger("relayDelay", relay.relayDelay) + synchronizedData.setInteger("relayAmount", relay.relayAmount) + synchronizedData.setInteger("maxQueueSize", relay.maxQueueSize) + synchronizedData.setInteger("packetsPerCycleAvg", relay.packetsPerCycleAvg()) + synchronizedData.setInteger("queueSize", relay.queue.size) + super.detectCustomDataChanges(nbt) + } +} diff --git a/src/main/scala/li/cil/oc/common/container/Switch.scala b/src/main/scala/li/cil/oc/common/container/Switch.scala index ae6c16254..78d884368 100644 --- a/src/main/scala/li/cil/oc/common/container/Switch.scala +++ b/src/main/scala/li/cil/oc/common/container/Switch.scala @@ -5,6 +5,7 @@ import li.cil.oc.common.tileentity import net.minecraft.entity.player.InventoryPlayer import net.minecraft.nbt.NBTTagCompound +// TODO Remove in 1.7 class Switch(playerInventory: InventoryPlayer, switch: tileentity.Switch) extends Player(playerInventory, switch) { addSlotToContainer(151, 15, Slot.CPU) addSlotToContainer(151, 34, Slot.Memory) diff --git a/src/main/scala/li/cil/oc/common/init/Blocks.scala b/src/main/scala/li/cil/oc/common/init/Blocks.scala index 1d3a6b21d..a3146cc5c 100644 --- a/src/main/scala/li/cil/oc/common/init/Blocks.scala +++ b/src/main/scala/li/cil/oc/common/init/Blocks.scala @@ -24,20 +24,22 @@ object Blocks { GameRegistry.registerTileEntity(classOf[tileentity.Geolyzer], Settings.namespace + "geolyzer") GameRegistry.registerTileEntity(classOf[tileentity.Microcontroller], Settings.namespace + "microcontroller") GameRegistry.registerTileEntity(classOf[tileentity.MotionSensor], Settings.namespace + "motionSensor") + GameRegistry.registerTileEntity(classOf[tileentity.NetSplitter], Settings.namespace + "netSplitter") GameRegistry.registerTileEntity(classOf[tileentity.PowerConverter], Settings.namespace + "powerConverter") GameRegistry.registerTileEntity(classOf[tileentity.PowerDistributor], Settings.namespace + "powerDistributor") GameRegistry.registerTileEntity(classOf[tileentity.Print], Settings.namespace + "print") GameRegistry.registerTileEntity(classOf[tileentity.Printer], Settings.namespace + "printer") GameRegistry.registerTileEntity(classOf[tileentity.Raid], Settings.namespace + "raid") GameRegistry.registerTileEntity(classOf[tileentity.Redstone], Settings.namespace + "redstone") + GameRegistry.registerTileEntity(classOf[tileentity.Relay], Settings.namespace + "relay") GameRegistry.registerTileEntity(classOf[tileentity.RobotProxy], Settings.namespace + "robot") GameRegistry.registerTileEntity(classOf[tileentity.Switch], Settings.namespace + "switch") GameRegistry.registerTileEntity(classOf[tileentity.Screen], Settings.namespace + "screen") GameRegistry.registerTileEntity(classOf[tileentity.ServerRack], Settings.namespace + "serverRack") - GameRegistry.registerTileEntity(classOf[tileentity.NetSplitter], Settings.namespace + "netSplitter") + GameRegistry.registerTileEntity(classOf[tileentity.Transposer], Settings.namespace + "transposer") GameRegistry.registerTileEntity(classOf[tileentity.Waypoint], Settings.namespace + "waypoint") - Recipes.addBlock(new AccessPoint(), Constants.BlockName.AccessPoint, "oc:accessPoint") + Items.registerBlock(new AccessPoint(), Constants.BlockName.AccessPoint) Recipes.addBlock(new Adapter(), Constants.BlockName.Adapter, "oc:adapter") Recipes.addBlock(new Assembler(), Constants.BlockName.Assembler, "oc:assembler") Recipes.addBlock(new Cable(), Constants.BlockName.Cable, "oc:cable") @@ -59,11 +61,12 @@ object Blocks { Recipes.addBlock(new Printer(), Constants.BlockName.Printer, "oc:printer") Recipes.addBlock(new Raid(), Constants.BlockName.Raid, "oc:raid") Recipes.addBlock(new Redstone(), Constants.BlockName.Redstone, "oc:redstone") + Recipes.addBlock(new Relay(), Constants.BlockName.Relay, "oc:relay") Recipes.addBlock(new Screen(Tier.One), Constants.BlockName.ScreenTier1, "oc:screen1") Recipes.addBlock(new Screen(Tier.Three), Constants.BlockName.ScreenTier3, "oc:screen3") Recipes.addBlock(new Screen(Tier.Two), Constants.BlockName.ScreenTier2, "oc:screen2") Recipes.addBlock(new ServerRack(), Constants.BlockName.ServerRack, "oc:serverRack") - Recipes.addBlock(new Switch(), Constants.BlockName.Switch, "oc:switch") + Items.registerBlock(new Switch(), Constants.BlockName.Switch) Recipes.addBlock(new Waypoint(), Constants.BlockName.Waypoint, "oc:waypoint") Items.registerBlock(new Case(Tier.Four), Constants.BlockName.CaseCreative) @@ -77,5 +80,8 @@ object Blocks { // v1.5.14 Recipes.addBlock(new NetSplitter(), Constants.BlockName.NetSplitter, "oc:netSplitter") + + // v1.5.16 + Recipes.addBlock(new Transposer(), Constants.BlockName.Transposer, "oc:transposer") } } diff --git a/src/main/scala/li/cil/oc/common/item/Tablet.scala b/src/main/scala/li/cil/oc/common/item/Tablet.scala index edc3070f6..14bd58b8c 100644 --- a/src/main/scala/li/cil/oc/common/item/Tablet.scala +++ b/src/main/scala/li/cil/oc/common/item/Tablet.scala @@ -241,9 +241,11 @@ class TabletWrapper(var stack: ItemStack, var player: EntityPlayer) extends Comp override def facing = RotationHelper.fromYaw(player.rotationYaw) - override def toLocal(value: EnumFacing) = value // -T-O-D-O- do we care? no we don't + override def toLocal(value: EnumFacing) = + RotationHelper.toLocal(EnumFacing.NORTH, facing, value) - override def toGlobal(value: EnumFacing) = value // -T-O-D-O- do we care? no we don't + override def toGlobal(value: EnumFacing) = + RotationHelper.toGlobal(EnumFacing.NORTH, facing, value) def readFromNBT() { if (stack.hasTagCompound) { diff --git a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala index 31d20a765..4b26ca417 100644 --- a/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala +++ b/src/main/scala/li/cil/oc/common/item/traits/CPULike.scala @@ -3,12 +3,10 @@ package li.cil.oc.common.item.traits import java.util import li.cil.oc.Settings -import li.cil.oc.api -import li.cil.oc.api.machine.Architecture +import li.cil.oc.integration.opencomputers.DriverCPU import li.cil.oc.util.Tooltip import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.ChatComponentTranslation import net.minecraft.world.World @@ -21,37 +19,19 @@ trait CPULike extends Delegate { override protected def tooltipData: Seq[Any] = Seq(Settings.get.cpuComponentSupport(cpuTier)) override protected def tooltipExtended(stack: ItemStack, tooltip: util.List[String]) { - (if (stack.hasTagCompound) { - Option(stack.getTagCompound.getString(Settings.namespace + "archName")) - } - else { - val architectures = allArchitectures - architectures.headOption.map(_._2) - }) match { - case Some(archName) if !archName.isEmpty => tooltip.addAll(Tooltip.get("CPU.Architecture", archName)) - case _ => allArchitectures.headOption.collect { - case ((_, name)) => tooltip.addAll(Tooltip.get("CPU.Architecture", name)) - } - } + tooltip.addAll(Tooltip.get("CPU.Architecture", DriverCPU.getArchitectureName(DriverCPU.architecture(stack)))) } override def onItemRightClick(stack: ItemStack, world: World, player: EntityPlayer) = { if (player.isSneaking) { if (!world.isRemote) { - val architectures = allArchitectures - if (architectures.length > 0) { - val currentIndex = if (stack.hasTagCompound) { - val currentArch = stack.getTagCompound.getString(Settings.namespace + "archClass") - architectures.indexWhere(_._1.getName == currentArch) - } - else { - stack.setTagCompound(new NBTTagCompound()) - -1 - } - val index = (currentIndex + 1) % architectures.length - val (archClass, archName) = architectures(index) - stack.getTagCompound.setString(Settings.namespace + "archClass", archClass.getName) - stack.getTagCompound.setString(Settings.namespace + "archName", archName) + val architectures = DriverCPU.allArchitectures.toList + if (architectures.nonEmpty) { + val currentIndex = architectures.indexOf(DriverCPU.architecture(stack)) + val newIndex = (currentIndex + 1) % architectures.length + val archClass = architectures(newIndex) + val archName = DriverCPU.getArchitectureName(archClass) + DriverCPU.setArchitecture(stack, archClass) player.addChatMessage(new ChatComponentTranslation(Settings.namespace + "tooltip.CPU.Architecture", archName)) } player.swingItem() @@ -59,11 +39,4 @@ trait CPULike extends Delegate { } stack } - - private def allArchitectures = api.Machine.architectures.map { arch => - arch.getAnnotation(classOf[Architecture.Name]) match { - case annotation: Architecture.Name => (arch, annotation.value) - case _ => (arch, arch.getSimpleName) - } - }.toArray } diff --git a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala index 34c19bd6b..5e4125310 100644 --- a/src/main/scala/li/cil/oc/common/recipe/Recipes.scala +++ b/src/main/scala/li/cil/oc/common/recipe/Recipes.scala @@ -326,6 +326,12 @@ object Recipes { GameRegistry.addRecipe(new ExtendedShapelessOreRecipe( lightPrint, print.createItemStack(1), new ItemStack(net.minecraft.init.Blocks.glowstone))) + + // Switch/AccessPoint -> Relay conversion + GameRegistry.addShapelessRecipe(api.Items.get(Constants.BlockName.Relay).createItemStack(1), + api.Items.get(Constants.BlockName.AccessPoint).createItemStack(1)) + GameRegistry.addShapelessRecipe(api.Items.get(Constants.BlockName.Relay).createItemStack(1), + api.Items.get(Constants.BlockName.Switch).createItemStack(1)) } catch { case e: Throwable => OpenComputers.log.error("Error parsing recipes, you may not be able to craft any items from this mod!", e) diff --git a/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala b/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala index 35b2746bc..63a195f23 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/AccessPoint.scala @@ -15,6 +15,7 @@ import net.minecraftforge.common.util.Constants.NBT import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly +// TODO Remove in 1.7 class AccessPoint extends Switch with WirelessEndpoint with traits.PowerAcceptor { var strength = Settings.get.maxWirelessRange @@ -24,6 +25,8 @@ class AccessPoint extends Switch with WirelessEndpoint with traits.PowerAcceptor withComponent("access_point"). create()) + override def isWirelessEnabled = true + // ----------------------------------------------------------------------- // @SideOnly(Side.CLIENT) @@ -71,7 +74,7 @@ class AccessPoint extends Switch with WirelessEndpoint with traits.PowerAcceptor override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet) { super.relayPacket(sourceSide, packet) - if (strength > 0 && (sourceSide != None || isRepeater)) { + if (strength > 0 && (sourceSide.isDefined || isRepeater)) { val cost = Settings.get.wirelessCostPerRange val tryChangeBuffer = sourceSide match { case Some(side) => diff --git a/src/main/scala/li/cil/oc/common/tileentity/Relay.scala b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala new file mode 100644 index 000000000..702d67638 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/Relay.scala @@ -0,0 +1,282 @@ +package li.cil.oc.common.tileentity + +import li.cil.oc.Constants +import li.cil.oc.Localization +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.Driver +import li.cil.oc.api.machine.Arguments +import li.cil.oc.api.machine.Callback +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network.Analyzable +import li.cil.oc.api.network.Connector +import li.cil.oc.api.network.Node +import li.cil.oc.api.network.Packet +import li.cil.oc.api.network.Visibility +import li.cil.oc.api.network.WirelessEndpoint +import li.cil.oc.common.InventorySlots +import li.cil.oc.common.Slot +import li.cil.oc.common.item +import li.cil.oc.common.item.Delegator +import li.cil.oc.integration.Mods +import li.cil.oc.integration.opencomputers.DriverLinkedCard +import li.cil.oc.server.network.QuantumNetwork +import li.cil.oc.util.ExtendedNBT._ +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.util.EnumFacing +import net.minecraftforge.common.util.Constants.NBT +import net.minecraftforge.fml.relauncher.Side +import net.minecraftforge.fml.relauncher.SideOnly + +class Relay extends traits.SwitchLike with traits.ComponentInventory with traits.PowerAcceptor with Analyzable with WirelessEndpoint with QuantumNetwork.QuantumNode { + lazy final val WirelessNetworkCard = api.Items.get(Constants.ItemName.WirelessNetworkCard) + lazy final val LinkedCard = api.Items.get(Constants.ItemName.LinkedCard) + + var strength = Settings.get.maxWirelessRange + + var isRepeater = true + + var isWirelessEnabled = false + + var isLinkedEnabled = false + + var tunnel = "creative" + + val componentNodes = Array.fill(6)(api.Network.newNode(this, Visibility.Network). + withComponent("relay"). + create()) + + override def canUpdate = isServer + + // ----------------------------------------------------------------------- // + + @SideOnly(Side.CLIENT) + override protected def hasConnector(side: EnumFacing) = true + + override protected def connector(side: EnumFacing) = sidedNode(side) match { + case connector: Connector => Option(connector) + case _ => None + } + + override def energyThroughput = Settings.get.accessPointRate + + // ----------------------------------------------------------------------- // + + override def onAnalyze(player: EntityPlayer, side: EnumFacing, hitX: Float, hitY: Float, hitZ: Float): Array[Node] = { + if (isWirelessEnabled) { + player.addChatMessage(Localization.Analyzer.WirelessStrength(strength)) + Array(componentNodes(side.getIndex)) + } + else null + } + + // ----------------------------------------------------------------------- // + + @Callback(direct = true, doc = """function():number -- Get the signal strength (range) used when relaying messages.""") + def getStrength(context: Context, args: Arguments): Array[AnyRef] = synchronized(result(strength)) + + @Callback(doc = """function(strength:number):number -- Set the signal strength (range) used when relaying messages.""") + def setStrength(context: Context, args: Arguments): Array[AnyRef] = synchronized { + strength = math.max(args.checkDouble(0), math.min(0, Settings.get.maxWirelessRange)) + result(strength) + } + + @Callback(direct = true, doc = """function():boolean -- Get whether the access point currently acts as a repeater (resend received wireless packets wirelessly).""") + def isRepeater(context: Context, args: Arguments): Array[AnyRef] = synchronized(result(isRepeater)) + + @Callback(doc = """function(enabled:boolean):boolean -- Set whether the access point should act as a repeater.""") + def setRepeater(context: Context, args: Arguments): Array[AnyRef] = synchronized { + isRepeater = args.checkBoolean(0) + result(isRepeater) + } + + // ----------------------------------------------------------------------- // + + protected def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) { + /* TODO ComputerCraft + for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { + val address = s"cc${computer.getID}_${computer.getAttachmentName}" + if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) + computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.map { + case x: Array[Byte] => new String(x, Charsets.UTF_8) + case x => x + }: _*)) + } + */ + } + + // ----------------------------------------------------------------------- // + + override def receivePacket(packet: Packet, source: WirelessEndpoint): Unit = { + if (isWirelessEnabled) { + tryEnqueuePacket(None, packet) + } + } + + override def receivePacket(packet: Packet): Unit = { + if (isLinkedEnabled) { + tryEnqueuePacket(None, packet) + } + } + + override def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet): Boolean = { + if (Mods.ComputerCraft.isAvailable) { + packet.data.headOption match { + case Some(answerPort: java.lang.Double) => queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1)) + case _ => queueMessage(packet.source, packet.destination, packet.port, -1, packet.data) + } + } + super.tryEnqueuePacket(sourceSide, packet) + } + + override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet): Unit = { + super.relayPacket(sourceSide, packet) + + val tryChangeBuffer = sourceSide match { + case Some(side) => + (amount: Double) => plugs(side.ordinal).node.asInstanceOf[Connector].tryChangeBuffer(amount) + case _ => + (amount: Double) => plugs.exists(_.node.asInstanceOf[Connector].tryChangeBuffer(amount)) + } + + if (isWirelessEnabled && strength > 0 && (sourceSide.isDefined || isRepeater)) { + val cost = Settings.get.wirelessCostPerRange + if (tryChangeBuffer(-strength * cost)) { + api.Network.sendWirelessPacket(this, strength, packet) + } + } + + if (isLinkedEnabled && sourceSide.isDefined) { + val cost = packet.size / 32.0 + Settings.get.wirelessCostPerRange * Settings.get.maxWirelessRange * 5 + if (tryChangeBuffer(-cost)) { + val endpoints = QuantumNetwork.getEndpoints(tunnel).filter(_ != this) + for (endpoint <- endpoints) { + endpoint.receivePacket(packet) + } + } + } + + onSwitchActivity() + } + + // ----------------------------------------------------------------------- // + + override protected def createNode(plug: Plug) = api.Network.newNode(plug, Visibility.Network). + withConnector(math.round(Settings.get.bufferAccessPoint)). + create() + + override protected def onPlugConnect(plug: Plug, node: Node) { + super.onPlugConnect(plug, node) + if (node == plug.node) { + api.Network.joinWirelessNetwork(this) + } + if (plug.isPrimary) + plug.node.connect(componentNodes(plug.side.ordinal())) + else + componentNodes(plug.side.ordinal).remove() + } + + override protected def onPlugDisconnect(plug: Plug, node: Node) { + super.onPlugDisconnect(plug, node) + if (node == plug.node) { + api.Network.leaveWirelessNetwork(this) + } + if (plug.isPrimary && node != plug.node) + plug.node.connect(componentNodes(plug.side.ordinal())) + else + componentNodes(plug.side.ordinal).remove() + } + + // ----------------------------------------------------------------------- // + + override protected def onItemAdded(slot: Int, stack: ItemStack) { + super.onItemAdded(slot, stack) + updateLimits(slot, stack) + } + + private def updateLimits(slot: Int, stack: ItemStack) { + Option(Driver.driverFor(stack, getClass)) match { + case Some(driver) if driver.slot(stack) == Slot.CPU => + relayDelay = math.max(1, relayBaseDelay - ((driver.tier(stack) + 1) * relayDelayPerUpgrade)) + case Some(driver) if driver.slot(stack) == Slot.Memory => + relayAmount = math.max(1, relayBaseAmount + (Delegator.subItem(stack) match { + case Some(ram: item.Memory) => (ram.tier + 1) * relayAmountPerUpgrade + case _ => (driver.tier(stack) + 1) * (relayAmountPerUpgrade * 2) + })) + case Some(driver) if driver.slot(stack) == Slot.HDD => + maxQueueSize = math.max(1, queueBaseSize + (driver.tier(stack) + 1) * queueSizePerUpgrade) + case Some(driver) if driver.slot(stack) == Slot.Card => + val descriptor = api.Items.get(stack) + if (descriptor == WirelessNetworkCard) { + isWirelessEnabled = true + } + if (descriptor == LinkedCard) { + val data = DriverLinkedCard.dataTag(stack) + if (data.hasKey(Settings.namespace + "tunnel")) { + tunnel = data.getString(Settings.namespace + "tunnel") + isLinkedEnabled = true + QuantumNetwork.add(this) + } + } + case _ => // Dafuq u doin. + } + } + + override protected def onItemRemoved(slot: Int, stack: ItemStack) { + super.onItemRemoved(slot, stack) + Driver.driverFor(stack, getClass) match { + case driver if driver.slot(stack) == Slot.CPU => relayDelay = relayBaseDelay + case driver if driver.slot(stack) == Slot.Memory => relayAmount = relayBaseAmount + case driver if driver.slot(stack) == Slot.HDD => maxQueueSize = queueBaseSize + case driver if driver.slot(stack) == Slot.Card => + isWirelessEnabled = false + isLinkedEnabled = false + QuantumNetwork.remove(this) + } + } + + override def getSizeInventory = InventorySlots.relay.length + + override def isItemValidForSlot(slot: Int, stack: ItemStack) = + Option(Driver.driverFor(stack, getClass)).fold(false)(driver => { + val provided = InventorySlots.relay(slot) + val tierSatisfied = driver.slot(stack) == provided.slot && driver.tier(stack) <= provided.tier + val cardTypeSatisfied = if (provided.slot == Slot.Card) api.Items.get(stack) == WirelessNetworkCard || api.Items.get(stack) == LinkedCard else true + tierSatisfied && cardTypeSatisfied + }) + + // ----------------------------------------------------------------------- // + + override def readFromNBTForServer(nbt: NBTTagCompound) { + super.readFromNBTForServer(nbt) + for (slot <- items.indices) items(slot) collect { + case stack => updateLimits(slot, stack) + } + + if (nbt.hasKey(Settings.namespace + "strength")) { + strength = nbt.getDouble(Settings.namespace + "strength") max 0 min Settings.get.maxWirelessRange + } + if (nbt.hasKey(Settings.namespace + "isRepeater")) { + isRepeater = nbt.getBoolean(Settings.namespace + "isRepeater") + } + nbt.getTagList(Settings.namespace + "componentNodes", NBT.TAG_COMPOUND).toArray[NBTTagCompound]. + zipWithIndex.foreach { + case (tag, index) => componentNodes(index).load(tag) + } + } + + override def writeToNBTForServer(nbt: NBTTagCompound) = { + super.writeToNBTForServer(nbt) + nbt.setDouble(Settings.namespace + "strength", strength) + nbt.setBoolean(Settings.namespace + "isRepeater", isRepeater) + nbt.setNewTagList(Settings.namespace + "componentNodes", componentNodes.map { + case node: Node => + val tag = new NBTTagCompound() + node.save(tag) + tag + case _ => new NBTTagCompound() + }) + } +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Switch.scala b/src/main/scala/li/cil/oc/common/tileentity/Switch.scala index 1fae1a4b7..1db46a684 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Switch.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Switch.scala @@ -6,25 +6,49 @@ import li.cil.oc.common.InventorySlots import li.cil.oc.common.Slot import li.cil.oc.common.item import li.cil.oc.common.item.Delegator -import li.cil.oc.server.PacketSender +import li.cil.oc.integration.Mods import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing -class Switch extends traits.Hub with traits.NotAnalyzable with traits.ComponentInventory { - var lastMessage = 0L +// TODO Remove in 1.7 +class Switch extends traits.SwitchLike with traits.NotAnalyzable with traits.ComponentInventory { + override def isWirelessEnabled = false + + override def isLinkedEnabled = false override def canUpdate = isServer // ----------------------------------------------------------------------- // + protected def queueMessage(source: String, destination: String, port: Int, answerPort: Int, args: Array[AnyRef]) { + /* TODO ComputerCraft + for (computer <- computers.map(_.asInstanceOf[IComputerAccess])) { + val address = s"cc${computer.getID}_${computer.getAttachmentName}" + if (source != address && Option(destination).forall(_ == address) && openPorts(computer).contains(port)) + computer.queueEvent("modem_message", Array(Seq(computer.getAttachmentName, Int.box(port), Int.box(answerPort)) ++ args.map { + case x: Array[Byte] => new String(x, Charsets.UTF_8) + case x => x + }: _*)) + } + */ + } + + // ----------------------------------------------------------------------- // + + override def tryEnqueuePacket(sourceSide: Option[EnumFacing], packet: Packet): Boolean = { + if (Mods.ComputerCraft.isAvailable) { + packet.data.headOption match { + case Some(answerPort: java.lang.Double) => queueMessage(packet.source, packet.destination, packet.port, answerPort.toInt, packet.data.drop(1)) + case _ => queueMessage(packet.source, packet.destination, packet.port, -1, packet.data) + } + } + super.tryEnqueuePacket(sourceSide, packet) + } + override protected def relayPacket(sourceSide: Option[EnumFacing], packet: Packet) { super.relayPacket(sourceSide, packet) - val now = System.currentTimeMillis() - if (now - lastMessage >= (relayDelay - 1) * 50) { - lastMessage = now - PacketSender.sendSwitchActivity(this) - } + onSwitchActivity() } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala b/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala new file mode 100644 index 000000000..b3e43b9f4 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/Transposer.scala @@ -0,0 +1,25 @@ +package li.cil.oc.common.tileentity + +import li.cil.oc.server.component +import net.minecraft.nbt.NBTTagCompound + +class Transposer extends traits.Environment { + val transposer = new component.Transposer(this) + + def node = transposer.node + + // Used on client side to check whether to render activity indicators. + var lastOperation = 0L + + override def canUpdate = false + + override def readFromNBTForServer(nbt: NBTTagCompound) { + super.readFromNBTForServer(nbt) + transposer.load(nbt) + } + + override def writeToNBTForServer(nbt: NBTTagCompound) { + super.writeToNBTForServer(nbt) + transposer.save(nbt) + } +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala index 9ccdc88cd..d7cb725a3 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/Rotatable.scala @@ -5,6 +5,7 @@ import li.cil.oc.common.block import li.cil.oc.server.{PacketSender => ServerPacketSender} import li.cil.oc.util.ExtendedEnumFacing._ import li.cil.oc.util.ExtendedWorld._ +import li.cil.oc.util.RotationHelper import net.minecraft.block.state.IBlockState import net.minecraft.entity.Entity import net.minecraft.util.EnumFacing @@ -15,69 +16,9 @@ trait Rotatable extends RotationAware with internal.Rotatable { // Lookup tables // ----------------------------------------------------------------------- // - /** - * Translates forge directions based on the block's pitch and yaw. The base - * forward direction is facing south with no pitch. The outer array is for - * the three different pitch states, the inner for the four different yaw - * states. - */ - private val translations = Array( - // Pitch = Down - Array( - // Yaw = North - Array(D.south, D.north, D.up, D.down, D.east, D.west), - // Yaw = South - Array(D.south, D.north, D.down, D.up, D.west, D.east), - // Yaw = West - Array(D.south, D.north, D.west, D.east, D.up, D.down), - // Yaw = East - Array(D.south, D.north, D.east, D.west, D.down, D.up)), - // Pitch = Up - Array( - // Yaw = North - Array(D.north, D.south, D.down, D.up, D.east, D.west), - // Yaw = South - Array(D.north, D.south, D.up, D.down, D.west, D.east), - // Yaw = West - Array(D.north, D.south, D.west, D.east, D.down, D.up), - // Yaw = East - Array(D.north, D.south, D.east, D.west, D.up, D.down)), - // Pitch = Forward (North|East|South|West) - Array( - // Yaw = North - Array(D.down, D.up, D.south, D.north, D.east, D.west), - // Yaw = South - Array(D.down, D.up, D.north, D.south, D.west, D.east), - // Yaw = West - Array(D.down, D.up, D.west, D.east, D.south, D.north), - // Yaw = East - Array(D.down, D.up, D.east, D.west, D.north, D.south))) + private val pitch2Direction = Array(EnumFacing.UP, EnumFacing.NORTH, EnumFacing.DOWN) - private val pitch2Direction = Array(D.up, D.north, D.down) - - private val yaw2Direction = Array(D.south, D.west, D.north, D.east) - - /** Shortcuts for forge directions to make the above more readable. */ - private object D { - val down = EnumFacing.DOWN - val up = EnumFacing.UP - val north = EnumFacing.NORTH - val south = EnumFacing.SOUTH - val west = EnumFacing.WEST - val east = EnumFacing.EAST - } - - // ----------------------------------------------------------------------- // - // State - // ----------------------------------------------------------------------- // - - /** Translation for facings based on current pitch and yaw. */ - private var cachedTranslation: Array[EnumFacing] = null - - /** Translation from local to global coordinates. */ - private var cachedInverseTranslation: Array[EnumFacing] = null - - protected var cacheDirty = true + private val yaw2Direction = Array(EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.EAST) // ----------------------------------------------------------------------- // // Accessors @@ -148,17 +89,9 @@ trait Rotatable extends RotationAware with internal.Rotatable { else false } - override def toLocal(value: EnumFacing) = if (value != null) this.synchronized { - updateTranslation() - cachedTranslation(value.ordinal) - } - else null + override def toLocal(value: EnumFacing) = if (value == null) null else RotationHelper.toLocal(pitch, yaw, value) - override def toGlobal(value: EnumFacing) = if (value != null) this.synchronized { - updateTranslation() - cachedInverseTranslation(value.ordinal) - } - else null + override def toGlobal(value: EnumFacing) = if (value == null) null else RotationHelper.toGlobal(pitch, yaw, value) def validFacings = Array(EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.EAST) @@ -177,16 +110,9 @@ trait Rotatable extends RotationAware with internal.Rotatable { // ----------------------------------------------------------------------- // /** Updates cached translation array and sends notification to clients. */ - protected def updateTranslation(): Unit = if (cacheDirty) { - cacheDirty = false - - val newTranslation = translations(pitch.ordinal)(yaw.ordinal - 2) - if (cachedTranslation != newTranslation) { - cachedTranslation = newTranslation - cachedInverseTranslation = invert(cachedTranslation) - if (world != null) { - onRotationChanged() - } + protected def updateTranslation(): Unit = { + if (world != null) { + onRotationChanged() } } @@ -196,7 +122,6 @@ trait Rotatable extends RotationAware with internal.Rotatable { def setState(newState: IBlockState): Boolean = { if (oldState.hashCode() != newState.hashCode()) { world.setBlockState(getPos, newState) - cacheDirty = true true } else false @@ -209,7 +134,4 @@ trait Rotatable extends RotationAware with internal.Rotatable { case _ => false } } - - private def invert(t: Array[EnumFacing]) = - t.indices.map(i => EnumFacing.getFront(t.indexOf(EnumFacing.getFront(i)))).toArray } diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala index 68ccc1773..aae9b9da0 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/RotatableTile.scala @@ -86,7 +86,6 @@ trait RotatableTile extends Rotatable { _yaw = yaw } if (changed) { - cacheDirty = true updateTranslation() } changed diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/SwitchLike.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/SwitchLike.scala new file mode 100644 index 000000000..9b14c128a --- /dev/null +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/SwitchLike.scala @@ -0,0 +1,27 @@ +package li.cil.oc.common.tileentity.traits + +import li.cil.oc.server.PacketSender + +import scala.collection.mutable + +trait SwitchLike extends Hub { + def relayDelay: Int + + def isWirelessEnabled: Boolean + + def isLinkedEnabled: Boolean + + val computers = mutable.Buffer.empty[AnyRef] + + val openPorts = mutable.Map.empty[AnyRef, mutable.Set[Int]] + + var lastMessage = 0L + + def onSwitchActivity(): Unit = { + val now = System.currentTimeMillis() + if (now - lastMessage >= (relayDelay - 1) * 50) { + lastMessage = now + PacketSender.sendSwitchActivity(this) + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/Mods.scala b/src/main/scala/li/cil/oc/integration/Mods.scala index b401e5492..292770682 100644 --- a/src/main/scala/li/cil/oc/integration/Mods.scala +++ b/src/main/scala/li/cil/oc/integration/Mods.scala @@ -19,8 +19,10 @@ object Mods { def All = knownMods.clone() + val AgriCraft = new SimpleMod(IDs.AgriCraft, version = "@[1.4.0,)") val AppliedEnergistics2 = new SimpleMod(IDs.AppliedEnergistics2, version = "@[rv1,)", providesPower = true) val BattleGear2 = new SimpleMod(IDs.BattleGear2) + val BetterRecords = new SimpleMod(IDs.BetterRecords) val BloodMagic = new SimpleMod(IDs.BloodMagic) val BluePower = new SimpleMod(IDs.BluePower, version = "@[0.2.928,)") val BuildCraft = new SimpleMod(IDs.BuildCraft) @@ -39,7 +41,7 @@ object Mods { val ElectricalAge = new SimpleMod(IDs.ElectricalAge, providesPower = true) val EnderIO = new SimpleMod(IDs.EnderIO) val EnderStorage = new SimpleMod(IDs.EnderStorage) - val ExtraCells = new SimpleMod(IDs.ExtraCells, version = "@[2.2.73,)") + val ExtraCells = new SimpleMod(IDs.ExtraCells, version = "@[2.2.73,)") val Factorization = new SimpleMod(IDs.Factorization, providesPower = true) val Forestry = new SimpleMod(IDs.Forestry) val ForgeMultipart = new SimpleMod(IDs.ForgeMultipart) @@ -84,7 +86,9 @@ object Mods { // ----------------------------------------------------------------------- // val Proxies = Array( + // integration.agricraft.ModAgriCraft, // integration.appeng.ModAppEng, + // integration.betterrecords.ModBetterRecords, // integration.bloodmagic.ModBloodMagic, // integration.bluepower.ModBluePower, // integration.buildcraft.library.ModBuildCraftAPILibrary, @@ -155,8 +159,10 @@ object Mods { // ----------------------------------------------------------------------- // object IDs { + final val AgriCraft = "AgriCraft" final val AppliedEnergistics2 = "appliedenergistics2" final val BattleGear2 = "battlegear2" + final val BetterRecords = "betterrecords" final val BloodMagic = "AWWayofTime" final val BluePower = "bluepowerAPI" final val BuildCraft = "BuildCraft|Core" diff --git a/src/main/scala/li/cil/oc/integration/agricraft/ApiHandler.scala b/src/main/scala/li/cil/oc/integration/agricraft/ApiHandler.scala new file mode 100644 index 000000000..03d5b3bc3 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/agricraft/ApiHandler.scala @@ -0,0 +1,17 @@ +package li.cil.oc.integration.agricraft + +import com.InfinityRaider.AgriCraft + +object ApiHandler { + lazy val Api = AgriCraft.api.API.getAPI(1) match { + case api: AgriCraft.api.v1.APIv1 if isApiUsable(api) => Option(api) + case _ => None + } + + private def isApiUsable(api: AgriCraft.api.APIBase) = { + val status = api.getStatus + status == AgriCraft.api.APIStatus.OK || + status == AgriCraft.api.APIStatus.BACKLEVEL_OK || + status == AgriCraft.api.APIStatus.BACKLEVEL_LIMITED + } +} diff --git a/src/main/scala/li/cil/oc/integration/agricraft/ConverterSeeds.scala b/src/main/scala/li/cil/oc/integration/agricraft/ConverterSeeds.scala new file mode 100644 index 000000000..e6b4c5a4d --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/agricraft/ConverterSeeds.scala @@ -0,0 +1,31 @@ +package li.cil.oc.integration.agricraft + +import java.util + +import com.InfinityRaider.AgriCraft.api.v1.ISeedStats +import li.cil.oc.api.driver.Converter +import net.minecraft.item.ItemStack + +import scala.collection.convert.WrapAsScala._ + +object ConverterSeeds extends Converter { + override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]): Unit = { + value match { + case stack: ItemStack => ApiHandler.Api.foreach(api => { + if (api.isHandledByAgricraft(stack) && stack.hasTagCompound && stack.getTagCompound.getBoolean("analyzed")) api.getSeedStats(stack) match { + case stats: ISeedStats => + output += "agricraft" -> Map( + "gain" -> float2Float(stats.getGain), + "maxGain" -> float2Float(stats.getMaxGain), + "growth" -> float2Float(stats.getGrowth), + "maxGrowth" -> float2Float(stats.getMaxGrowth), + "strength" -> float2Float(stats.getStrength), + "maxStrength" -> float2Float(stats.getMaxStrength) + ) + case _ => + } + }) + case _ => + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/agricraft/EventHandlerAgriCraft.scala b/src/main/scala/li/cil/oc/integration/agricraft/EventHandlerAgriCraft.scala new file mode 100644 index 000000000..5dc941772 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/agricraft/EventHandlerAgriCraft.scala @@ -0,0 +1,31 @@ +package li.cil.oc.integration.agricraft + +import com.InfinityRaider.AgriCraft.api.v1.ISeedStats +import cpw.mods.fml.common.eventhandler.SubscribeEvent +import li.cil.oc.api.event.GeolyzerEvent + +import scala.collection.convert.WrapAsScala._ + +object EventHandlerAgriCraft { + @SubscribeEvent + def onGeolyzerAnalyze(e: GeolyzerEvent.Analyze) { + val world = e.host.world + + ApiHandler.Api.foreach(api => if (api.isCrops(world, e.x, e.y, e.z)) { + e.data += "growth" -> float2Float(if (api.isMature(world, e.x, e.y, e.z)) 1f else 0f) + + if (api.isAnalyzed(world, e.x, e.y, e.z)) { + api.getStats(world, e.x, e.y, e.z) match { + case stats: ISeedStats => + e.data += "gain" -> float2Float(stats.getGain) + e.data += "maxGain" -> float2Float(stats.getMaxGain) + e.data += "growth" -> float2Float(stats.getGrowth) + e.data += "maxGrowth" -> float2Float(stats.getMaxGrowth) + e.data += "strength" -> float2Float(stats.getStrength) + e.data += "maxStrength" -> float2Float(stats.getMaxStrength) + case _ => // Invalid crop. + } + } + }) + } +} diff --git a/src/main/scala/li/cil/oc/integration/agricraft/ModAgriCraft.scala b/src/main/scala/li/cil/oc/integration/agricraft/ModAgriCraft.scala new file mode 100644 index 000000000..f3325428f --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/agricraft/ModAgriCraft.scala @@ -0,0 +1,17 @@ +package li.cil.oc.integration.agricraft + +import li.cil.oc.api.Driver +import li.cil.oc.integration.Mod +import li.cil.oc.integration.ModProxy +import li.cil.oc.integration.Mods +import net.minecraftforge.common.MinecraftForge + +object ModAgriCraft extends ModProxy { + override def getMod: Mod = Mods.AgriCraft + + override def initialize(): Unit = { + Driver.add(ConverterSeeds) + + MinecraftForge.EVENT_BUS.register(EventHandlerAgriCraft) + } +} diff --git a/src/main/scala/li/cil/oc/integration/betterrecords/ConverterRecord.scala b/src/main/scala/li/cil/oc/integration/betterrecords/ConverterRecord.scala new file mode 100644 index 000000000..ad145e330 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/betterrecords/ConverterRecord.scala @@ -0,0 +1,40 @@ +package li.cil.oc.integration.betterrecords + +import java.util + +import li.cil.oc.api.driver.Converter +import li.cil.oc.util.ExtendedNBT._ +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraftforge.common.util.Constants.NBT + +import scala.collection.convert.WrapAsScala._ + +object ConverterRecord extends Converter { + final val UrlRecordClassName = "com.codingforcookies.betterrecords.src.items.ItemURLRecord" + final val UrlMultiRecordClassName = "com.codingforcookies.betterrecords.src.items.ItemURLMultiRecord" + final val FreqCrystalClassName = "com.codingforcookies.betterrecords.src.items.ItemFreqCrystal" + + override def convert(value: scala.Any, output: util.Map[AnyRef, AnyRef]): Unit = value match { + case stack: ItemStack if stack.getItem != null && stack.getItem.getClass.getName == UrlRecordClassName && stack.hasTagCompound => + convertRecord(stack.getTagCompound, output) + case stack: ItemStack if stack.getItem != null && stack.getItem.getClass.getName == UrlMultiRecordClassName && stack.hasTagCompound => + output += "songs" -> stack.getTagCompound.getTagList("songs", NBT.TAG_COMPOUND).map((nbt: NBTTagCompound) => convertRecord(nbt, new util.HashMap[AnyRef, AnyRef]())) + case stack: ItemStack if stack.getItem != null && stack.getItem.getClass.getName == FreqCrystalClassName && stack.hasTagCompound => + convertRecord(stack.getTagCompound, output) + case _ => + } + + private def convertRecord(nbt: NBTTagCompound, output: util.Map[AnyRef, AnyRef]) = { + if (nbt.hasKey("url", NBT.TAG_STRING)) + output += "url" -> nbt.getString("url") + if (nbt.hasKey("name", NBT.TAG_STRING)) + output += "filename" -> nbt.getString("name") + if (nbt.hasKey("author", NBT.TAG_STRING)) + output += "author" -> nbt.getString("author") + if (nbt.hasKey("local", NBT.TAG_STRING)) + output += "title" -> nbt.getString("local") + + output + } +} diff --git a/src/main/scala/li/cil/oc/integration/betterrecords/ModBetterRecords.scala b/src/main/scala/li/cil/oc/integration/betterrecords/ModBetterRecords.scala new file mode 100644 index 000000000..258b557af --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/betterrecords/ModBetterRecords.scala @@ -0,0 +1,13 @@ +package li.cil.oc.integration.betterrecords + +import li.cil.oc.api.Driver +import li.cil.oc.integration.ModProxy +import li.cil.oc.integration.Mods + +object ModBetterRecords extends ModProxy { + override def getMod = Mods.BetterRecords + + override def initialize() { + Driver.add(ConverterRecord) + } +} diff --git a/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala b/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala index b2e54e940..5f17cf9df 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/PeripheralProvider.scala @@ -2,7 +2,7 @@ package li.cil.oc.integration.computercraft import dan200.computercraft.api.ComputerCraftAPI import dan200.computercraft.api.peripheral.IPeripheralProvider -import li.cil.oc.common.tileentity.Switch +import li.cil.oc.common.tileentity.traits.SwitchLike import net.minecraft.world.World object PeripheralProvider extends IPeripheralProvider { @@ -11,7 +11,7 @@ object PeripheralProvider extends IPeripheralProvider { } override def getPeripheral(world: World, x: Int, y: Int, z: Int, side: Int) = world.getTileEntity(x, y, z) match { - case switch: Switch => new SwitchPeripheral(switch) + case switch: SwitchLike => new SwitchPeripheral(switch) case _ => null } } diff --git a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala index 804b6bb47..234e93e4b 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/SwitchPeripheral.scala @@ -8,8 +8,7 @@ import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.machine.Context import li.cil.oc.api.network.Component -import li.cil.oc.common.tileentity.AccessPoint -import li.cil.oc.common.tileentity.Switch +import li.cil.oc.common.tileentity.traits.SwitchLike import li.cil.oc.util.ResultWrapper._ import net.minecraftforge.common.util.ForgeDirection @@ -17,7 +16,7 @@ import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ import scala.collection.mutable -class SwitchPeripheral(val switch: Switch) extends IPeripheral { +class SwitchPeripheral(val switch: SwitchLike) extends IPeripheral { private val methods = Map[String, (IComputerAccess, ILuaContext, Array[AnyRef]) => Array[AnyRef]]( // Generic modem methods. "open" -> ((computer, context, arguments) => { @@ -86,7 +85,10 @@ class SwitchPeripheral(val switch: Switch) extends IPeripheral { // OC specific. "isAccessPoint" -> ((computer, context, arguments) => { - result(switch.isInstanceOf[AccessPoint]) + result(switch.isWirelessEnabled) + }), + "isTunnel" -> ((computer, context, arguments) => { + result(switch.isLinkedEnabled) }), "maxPacketSize" -> ((computer, context, arguments) => { result(Settings.get.maxNetworkPacketSize) diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala index 14db12664..62e8557d3 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverCPU.scala @@ -5,7 +5,7 @@ import li.cil.oc.OpenComputers import li.cil.oc.Settings import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentHost -import li.cil.oc.api.driver.item.Processor +import li.cil.oc.api.driver.item.MutableProcessor import li.cil.oc.api.machine.Architecture import li.cil.oc.api.network.ManagedEnvironment import li.cil.oc.common.Slot @@ -14,12 +14,14 @@ import li.cil.oc.common.item import li.cil.oc.common.item.Delegator import li.cil.oc.server.machine.luac.NativeLuaArchitecture import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import scala.collection.convert.WrapAsJava._ import scala.collection.convert.WrapAsScala._ object DriverCPU extends DriverCPU -abstract class DriverCPU extends Item with Processor { +abstract class DriverCPU extends Item with MutableProcessor { override def worksWith(stack: ItemStack) = isOneOf(stack, api.Items.get(Constants.ItemName.CPUTier1), api.Items.get(Constants.ItemName.CPUTier2), @@ -39,6 +41,8 @@ abstract class DriverCPU extends Item with Processor { override def supportedComponents(stack: ItemStack) = Settings.get.cpuComponentSupport(cpuTier(stack)) + override def allArchitectures = api.Machine.architectures.toList + override def architecture(stack: ItemStack): Class[_ <: Architecture] = { if (stack.hasTagCompound) { val archClass = stack.getTagCompound.getString(Settings.namespace + "archClass") match { @@ -57,4 +61,18 @@ abstract class DriverCPU extends Item with Processor { } api.Machine.architectures.headOption.orNull } + + override def setArchitecture(stack: ItemStack, architecture: Class[_ <: Architecture]): Unit = { + if (!worksWith(stack)) throw new IllegalArgumentException("Unsupported processor type.") + if (!stack.hasTagCompound) stack.setTagCompound(new NBTTagCompound()) + stack.getTagCompound.setString(Settings.namespace + "archClass", architecture.getName) + stack.getTagCompound.setString(Settings.namespace + "archName", getArchitectureName(architecture)) + } + + // TODO Move to Machine API in 1.6 + def getArchitectureName(architecture: Class[_ <: Architecture]) = + architecture.getAnnotation(classOf[Architecture.Name]) match { + case annotation: Architecture.Name => annotation.value + case _ => architecture.getSimpleName + } } diff --git a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala index 3ffb00733..b58201993 100644 --- a/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala +++ b/src/main/scala/li/cil/oc/integration/opencomputers/DriverUpgradePiston.scala @@ -5,7 +5,7 @@ import li.cil.oc.api import li.cil.oc.api.driver.EnvironmentAware import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.driver.item.HostAware -import li.cil.oc.api.internal.Rotatable +import li.cil.oc.api.internal import li.cil.oc.common.Slot import li.cil.oc.server.component import net.minecraft.item.ItemStack @@ -15,7 +15,9 @@ object DriverUpgradePiston extends Item with HostAware with EnvironmentAware { api.Items.get(Constants.ItemName.PistonUpgrade)) override def createEnvironment(stack: ItemStack, host: EnvironmentHost) = host match { - case rotatable: Rotatable with EnvironmentHost => new component.UpgradePiston(rotatable) + case host: internal.Drone => new component.UpgradePiston.Drone(host) + case host: internal.Tablet => new component.UpgradePiston.Tablet(host) + case host: internal.Rotatable with EnvironmentHost => new component.UpgradePiston.Rotatable(host) case _ => null } diff --git a/src/main/scala/li/cil/oc/integration/vanilla/EventHandlerVanilla.scala b/src/main/scala/li/cil/oc/integration/vanilla/EventHandlerVanilla.scala new file mode 100644 index 000000000..60ff823af --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/vanilla/EventHandlerVanilla.scala @@ -0,0 +1,29 @@ +package li.cil.oc.integration.vanilla + +import li.cil.oc.api.event.GeolyzerEvent +import net.minecraft.block.BlockCrops +import net.minecraft.init.Blocks +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +import scala.collection.convert.WrapAsScala._ + +object EventHandlerVanilla { + @SubscribeEvent + def onGeolyzerAnalyze(e: GeolyzerEvent.Analyze) { + val world = e.host.world + val blockState = world.getBlockState(e.pos) + val block = blockState.getBlock + if (block.isInstanceOf[BlockCrops] || block == Blocks.melon_stem || block == Blocks.pumpkin_stem || block == Blocks.carrots || block == Blocks.potatoes) { + e.data += "growth" -> float2Float((block.getMetaFromState(blockState) / 7f) max 0 min 1) + } + if (block == Blocks.cocoa) { + e.data += "growth" -> float2Float(((block.getMetaFromState(blockState) >> 2) / 2f) max 0 min 1) + } + if (block == Blocks.nether_wart) { + e.data += "growth" -> float2Float((block.getMetaFromState(blockState) / 3f) max 0 min 1) + } + if (block == Blocks.melon_block || block == Blocks.pumpkin || block == Blocks.cactus || block == Blocks.reeds) { + e.data += "growth" -> float2Float(1f) + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala b/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala index 5b7b97b5f..c95903413 100644 --- a/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala +++ b/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala @@ -11,6 +11,7 @@ import li.cil.oc.util.ExtendedWorld._ import net.minecraft.block.BlockRedstoneWire import net.minecraft.init.Blocks import net.minecraft.util.EnumFacing +import net.minecraftforge.common.MinecraftForge object ModVanilla extends ModProxy with RedstoneProvider { def getMod = Mods.Minecraft @@ -45,6 +46,8 @@ object ModVanilla extends ModProxy with RedstoneProvider { RecipeHandler.init() BundledRedstone.addProvider(this) + + MinecraftForge.EVENT_BUS.register(EventHandlerVanilla) } override def computeInput(pos: BlockPosition, side: EnumFacing): Int = { diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index d710bcd5f..9975e0deb 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -227,6 +227,16 @@ object PacketSender { } } + def sendNetSplitterState(t: tileentity.NetSplitter): Unit = { + val pb = new SimplePacketBuilder(PacketType.NetSplitterState) + + pb.writeTileEntity(t) + pb.writeBoolean(t.isInverted) + pb.writeByte(t.compressSides) + + pb.sendToPlayersNearTileEntity(t) + } + def sendParticleEffect(position: BlockPosition, particleType: EnumParticleTypes, count: Int, velocity: Double, direction: Option[EnumFacing] = None): Unit = if (count > 0) { val pb = new SimplePacketBuilder(PacketType.ParticleEffect) @@ -385,7 +395,7 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } - def sendSwitchActivity(t: tileentity.Switch) { + def sendSwitchActivity(t: tileentity.traits.SwitchLike) { val pb = new SimplePacketBuilder(PacketType.SwitchActivity) pb.writeTileEntity(t) @@ -522,16 +532,6 @@ object PacketSender { pb.sendToPlayersNearHost(host) } - def sendNetSplitterState(t: tileentity.NetSplitter): Unit = { - val pb = new SimplePacketBuilder(PacketType.NetSplitterState) - - pb.writeTileEntity(t) - pb.writeBoolean(t.isInverted) - pb.writeByte(t.compressSides) - - pb.sendToPlayersNearTileEntity(t) - } - def sendScreenTouchMode(t: tileentity.Screen, value: Boolean) { val pb = new SimplePacketBuilder(PacketType.ScreenTouchMode) @@ -612,6 +612,14 @@ object PacketSender { pb.sendToNearbyPlayers(world, x, y, z, Option(32)) } + def sendTransposerActivity(t: tileentity.Transposer) { + val pb = new SimplePacketBuilder(PacketType.TransposerActivity) + + pb.writeTileEntity(t) + + pb.sendToPlayersNearTileEntity(t, Option(32)) + } + def sendWaypointLabel(t: Waypoint): Unit = { val pb = new SimplePacketBuilder(PacketType.WaypointLabel) diff --git a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala index 4ad804a3b..e90de08bb 100644 --- a/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala +++ b/src/main/scala/li/cil/oc/server/component/GraphicsCard.scala @@ -55,22 +55,26 @@ abstract class GraphicsCard extends prefab.ManagedEnvironment { } } - @Callback(doc = """function(address:string):boolean -- Binds the GPU to the screen with the specified address.""") + @Callback(doc = """function(address:string[, reset:boolean=true]):boolean -- Binds the GPU to the screen with the specified address and resets screen settings if `reset` is true.""") def bind(context: Context, args: Arguments): Array[AnyRef] = { val address = args.checkString(0) + val reset = args.optBoolean(1, true) node.network.node(address) match { case null => result(Unit, "invalid address") case node: Node if node.host.isInstanceOf[TextBuffer] => screenAddress = Option(address) screenInstance = Some(node.host.asInstanceOf[TextBuffer]) screen(s => { - val (gmw, gmh) = maxResolution - val smw = s.getMaximumWidth - val smh = s.getMaximumHeight - s.setResolution(math.min(gmw, smw), math.min(gmh, smh)) - s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal))) - s.setForegroundColor(0xFFFFFF) - s.setBackgroundColor(0x000000) + if (reset) { + val (gmw, gmh) = maxResolution + val smw = s.getMaximumWidth + val smh = s.getMaximumHeight + s.setResolution(math.min(gmw, smw), math.min(gmh, smh)) + s.setColorDepth(ColorDepth.values.apply(math.min(maxDepth.ordinal, s.getMaximumColorDepth.ordinal))) + s.setForegroundColor(0xFFFFFF) + s.setBackgroundColor(0x000000) + } + else context.pause(0.2) // To discourage outputting "in realtime" to multiple screens using one GPU. result(true) }) case _ => result(Unit, "not a screen") diff --git a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala index 3d3c27ad5..980609515 100644 --- a/src/main/scala/li/cil/oc/server/component/LinkedCard.scala +++ b/src/main/scala/li/cil/oc/server/component/LinkedCard.scala @@ -12,7 +12,7 @@ import net.minecraft.nbt.NBTTagCompound import scala.collection.convert.WrapAsScala._ -class LinkedCard extends prefab.ManagedEnvironment { +class LinkedCard extends prefab.ManagedEnvironment with QuantumNetwork.QuantumNode { override val node = Network.newNode(this, Visibility.Network). withComponent("tunnel", Visibility.Neighbors). withConnector(). @@ -28,8 +28,8 @@ class LinkedCard extends prefab.ManagedEnvironment { // Cast to iterable to use Scala's toArray instead of the Arguments' one (which converts byte arrays to Strings). val packet = Network.newPacket(node.address, null, 0, args.asInstanceOf[java.lang.Iterable[AnyRef]].toArray) if (node.tryChangeBuffer(-(packet.size / 32.0 + Settings.get.wirelessCostPerRange * Settings.get.maxWirelessRange * 5))) { - for (card <- endpoints) { - card.receivePacket(packet) + for (endpoint <- endpoints) { + endpoint.receivePacket(packet) } result(true) } diff --git a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala index 111e76ff2..a62f92b10 100644 --- a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala @@ -24,6 +24,8 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { protected var wakeMessage: Option[String] = None + protected var wakeMessageFuzzy = false + // ----------------------------------------------------------------------- // @Callback(doc = """function(port:number):boolean -- Opens the specified port. Returns true if the port was opened.""") @@ -39,7 +41,7 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { @Callback(doc = """function([port:number]):boolean -- Closes the specified port (default: all ports). Returns true if ports were closed.""") def close(context: Context, args: Arguments): Array[AnyRef] = { if (args.count == 0) { - val closed = openPorts.size > 0 + val closed = openPorts.nonEmpty openPorts.clear() result(closed) } @@ -78,17 +80,21 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { @Callback(direct = true, doc = """function():number -- Gets the maximum packet size (config setting).""") def maxPacketSize(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.maxNetworkPacketSize) - @Callback(direct = true, doc = """function():string -- Get the current wake-up message.""") - def getWakeMessage(context: Context, args: Arguments): Array[AnyRef] = result(wakeMessage.orNull) + @Callback(direct = true, doc = """function():string, boolean -- Get the current wake-up message.""") + def getWakeMessage(context: Context, args: Arguments): Array[AnyRef] = result(wakeMessage.orNull, wakeMessageFuzzy) - @Callback(doc = """function(message:string):string -- Set the wake-up message.""") + @Callback(doc = """function(message:string[, fuzzy:boolean]):string -- Set the wake-up message and whether to ignore additional data/parameters.""") def setWakeMessage(context: Context, args: Arguments): Array[AnyRef] = { val oldMessage = wakeMessage + val oldFuzzy = wakeMessageFuzzy + if (args.optAny(0, null) == null) wakeMessage = None else wakeMessage = Option(args.checkString(0)) - result(oldMessage.orNull) + wakeMessageFuzzy = args.optBoolean(1, wakeMessageFuzzy) + + result(oldMessage.orNull, oldFuzzy) } protected def doSend(packet: Packet) { @@ -136,6 +142,10 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { node.sendToNeighbors("computer.start") case Array(message: String) if wakeMessage.contains(message) => node.sendToNeighbors("computer.start") + case Array(message: Array[Byte], _*) if wakeMessageFuzzy && wakeMessage.contains(new String(message, Charsets.UTF_8)) => + node.sendToNeighbors("computer.start") + case Array(message: String, _*) if wakeMessageFuzzy && wakeMessage.contains(message) => + node.sendToNeighbors("computer.start") case _ => } } @@ -151,6 +161,7 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { if (nbt.hasKey("wakeMessage")) { wakeMessage = Option(nbt.getString("wakeMessage")) } + wakeMessageFuzzy = nbt.getBoolean("wakeMessageFuzzy") } override def save(nbt: NBTTagCompound) { @@ -158,6 +169,7 @@ class NetworkCard(val host: EnvironmentHost) extends prefab.ManagedEnvironment { nbt.setIntArray("openPorts", openPorts.toArray) wakeMessage.foreach(nbt.setString("wakeMessage", _)) + nbt.setBoolean("wakeMessageFuzzy", wakeMessageFuzzy) } // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala index bfa7120cb..fd6f6ecac 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneBundled.scala @@ -1,5 +1,6 @@ package li.cil.oc.server.component +import li.cil.oc.Settings import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback @@ -36,14 +37,16 @@ trait RedstoneBundled extends RedstoneVanilla { case (color, number: Number) => redstone.bundledOutput(side, color, number.intValue()) case _ => } - context.pause(0.1) + if (Settings.get.redstoneDelay > 0) + context.pause(Settings.get.redstoneDelay) result(true) } else { val color = checkColor(args, 1) val value = args.checkInteger(2) redstone.bundledOutput(side, color, value) - context.pause(0.1) + if (Settings.get.redstoneDelay > 0) + context.pause(Settings.get.redstoneDelay) result(redstone.bundledOutput(side, color)) } } diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala index f9de50dca..f5721494f 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneVanilla.scala @@ -1,5 +1,6 @@ package li.cil.oc.server.component +import li.cil.oc.Settings import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback @@ -30,7 +31,8 @@ trait RedstoneVanilla extends RedstoneSignaller { val side = checkSide(args, 0) val value = args.checkInteger(1) redstone.output(side, value) - context.pause(0.1) + if (Settings.get.redstoneDelay > 0) + context.pause(Settings.get.redstoneDelay) result(redstone.output(side)) } diff --git a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala index 04760308a..5337c6869 100644 --- a/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala +++ b/src/main/scala/li/cil/oc/server/component/RedstoneWireless.scala @@ -6,6 +6,7 @@ import codechicken.wirelessredstone.core.WirelessReceivingDevice import codechicken.wirelessredstone.core.WirelessTransmittingDevice */ +import li.cil.oc.Settings import li.cil.oc.api.driver.EnvironmentHost import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback @@ -44,11 +45,17 @@ trait RedstoneWireless extends RedstoneSignaller /* with WirelessReceivingDevice @Callback(doc = """function(value:boolean):boolean -- Set the wireless redstone output.""") def setWirelessOutput(context: Context, args: Arguments): Array[AnyRef] = { val oldValue = wirelessOutput - wirelessOutput = args.checkBoolean(0) + val newValue = args.checkBoolean(0) - util.WirelessRedstone.updateOutput(this) + if (oldValue != newValue) { + wirelessOutput = newValue + + util.WirelessRedstone.updateOutput(this) + + if (Settings.get.redstoneDelay > 0) + context.pause(Settings.get.redstoneDelay) + } - context.pause(0.1) result(oldValue) } @@ -60,16 +67,19 @@ trait RedstoneWireless extends RedstoneSignaller /* with WirelessReceivingDevice val oldValue = wirelessFrequency val newValue = args.checkInteger(0) - util.WirelessRedstone.removeReceiver(this) - util.WirelessRedstone.removeTransmitter(this) + if (oldValue != newValue) { + util.WirelessRedstone.removeReceiver(this) + util.WirelessRedstone.removeTransmitter(this) - wirelessFrequency = newValue - wirelessInput = false - wirelessOutput = false + wirelessFrequency = newValue + wirelessInput = false + wirelessOutput = false - util.WirelessRedstone.addReceiver(this) + util.WirelessRedstone.addReceiver(this) + + context.pause(0.5) + } - context.pause(0.5) result(oldValue) } diff --git a/src/main/scala/li/cil/oc/server/component/Transposer.scala b/src/main/scala/li/cil/oc/server/component/Transposer.scala new file mode 100644 index 000000000..c51faeaf2 --- /dev/null +++ b/src/main/scala/li/cil/oc/server/component/Transposer.scala @@ -0,0 +1,69 @@ +package li.cil.oc.server.component + +import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.machine.Arguments +import li.cil.oc.api.machine.Callback +import li.cil.oc.api.machine.Context +import li.cil.oc.api.network.Visibility +import li.cil.oc.api.prefab +import li.cil.oc.common.tileentity +import li.cil.oc.server.{PacketSender => ServerPacketSender} +import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedArguments._ +import li.cil.oc.util.FluidUtils +import li.cil.oc.util.InventoryUtils +import net.minecraft.util.EnumFacing + +import scala.language.existentials + +class Transposer(val host: tileentity.Transposer) extends prefab.ManagedEnvironment with traits.WorldInventoryAnalytics with traits.WorldTankAnalytics { + override val node = api.Network.newNode(this, Visibility.Network). + withComponent("transposer"). + withConnector(). + create() + + override def position = BlockPosition(host) + + override protected def checkSideForAction(args: Arguments, n: Int) = args.checkSide(n, EnumFacing.values: _*) + + @Callback(doc = """function(sourceSide:number, sinkSide:number[, count:number[, sourceSlot:number, sinkSlot:number]]):number -- Transfer some items between two inventories.""") + def transferItem(context: Context, args: Arguments): Array[AnyRef] = { + val sourceSide = checkSideForAction(args, 0) + val sourcePos = position.offset(sourceSide) + val sinkSide = checkSideForAction(args, 1) + val sinkPos = position.offset(sinkSide) + val count = args.optItemCount(2) + + if (node.tryChangeBuffer(-Settings.get.transposerCost)) { + ServerPacketSender.sendTransposerActivity(host) + + if (args.count > 3) { + val sourceSlot = args.checkSlot(InventoryUtils.inventoryAt(sourcePos).getOrElse(throw new IllegalArgumentException("no inventory")), 3) + val sinkSlot = args.checkSlot(InventoryUtils.inventoryAt(sinkPos).getOrElse(throw new IllegalArgumentException("no inventory")), 4) + + result(InventoryUtils.transferBetweenInventoriesSlotsAt(sourcePos, sourceSide.getOpposite, sourceSlot, sinkPos, Option(sinkSide.getOpposite), sinkSlot, count)) + } + else result(InventoryUtils.transferBetweenInventoriesAt(sourcePos, sourceSide.getOpposite, sinkPos, Option(sinkSide.getOpposite), count)) + } + else result(Unit, "not enough energy") + } + + @Callback(doc = """function(sourceSide:number, sinkSide:number[, count:number]):number -- Transfer some items between two inventories.""") + def transferFluid(context: Context, args: Arguments): Array[AnyRef] = { + val sourceSide = checkSideForAction(args, 0) + val sourcePos = position.offset(sourceSide) + val sinkSide = checkSideForAction(args, 1) + val sinkPos = position.offset(sinkSide) + val count = args.optFluidCount(2) + + if (node.tryChangeBuffer(-Settings.get.transposerCost)) { + ServerPacketSender.sendTransposerActivity(host) + + val moved = FluidUtils.transferBetweenFluidHandlersAt(sourcePos, sourceSide.getOpposite, sinkPos, sinkSide.getOpposite, count) + if (moved > 0) context.pause(moved / 1000 * 0.25) // Allow up to 4 buckets per second. + result(moved > 0, moved) + } + else result(Unit, "not enough energy") + } +} diff --git a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala index 1b0912c12..01adf59a1 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradePiston.scala @@ -3,27 +3,34 @@ package li.cil.oc.server.component import li.cil.oc.Settings import li.cil.oc.api.Network import li.cil.oc.api.driver.EnvironmentHost -import li.cil.oc.api.internal.Rotatable +import li.cil.oc.api.internal import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network.Visibility import li.cil.oc.api.prefab import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedArguments._ import li.cil.oc.util.ExtendedWorld._ import net.minecraft.init.Blocks +import net.minecraft.util.EnumFacing -class UpgradePiston(val host: Rotatable with EnvironmentHost) extends prefab.ManagedEnvironment { +abstract class UpgradePiston(val host: EnvironmentHost) extends prefab.ManagedEnvironment { override val node = Network.newNode(this, Visibility.Network). withComponent("piston"). withConnector(). create() - @Callback(doc = """function():boolean -- Tries to push the block in front of the container of the upgrade.""") + def pushDirection(args: Arguments, index: Int): EnumFacing + + def pushOrigin(side: EnumFacing) = BlockPosition(host) + + @Callback(doc = """function([side:number]):boolean -- Tries to push the block on the specified side of the container of the upgrade. Defaults to front.""") def push(context: Context, args: Arguments): Array[AnyRef] = { - val hostPos = BlockPosition(host) - val blockPos = hostPos.offset(host.facing) - if (!host.world.isAirBlock(blockPos) && node.tryChangeBuffer(-Settings.get.pistonCost) && Blocks.piston.doMove(host.world, hostPos.toBlockPos, host.facing, true)) { + val side = pushDirection(args, 0) + val hostPos = pushOrigin(side) + val blockPos = hostPos.offset(side) + if (!host.world.isAirBlock(blockPos) && node.tryChangeBuffer(-Settings.get.pistonCost) && Blocks.piston.doMove(host.world, hostPos.toBlockPos, side, true)) { host.world.setBlockToAir(blockPos) host.world.playSoundEffect(host.xPosition, host.yPosition, host.zPosition, "tile.piston.out", 0.5f, host.world.rand.nextFloat() * 0.25f + 0.6f) context.pause(0.5) @@ -32,3 +39,21 @@ class UpgradePiston(val host: Rotatable with EnvironmentHost) extends prefab.Man else result(false) } } + +object UpgradePiston { + + class Drone(drone: internal.Drone) extends UpgradePiston(drone) { + override def pushDirection(args: Arguments, index: Int) = args.optSide(index, EnumFacing.SOUTH, EnumFacing.values: _*) + } + + class Tablet(tablet: internal.Tablet) extends Rotatable(tablet) { + override def pushOrigin(side: EnumFacing) = + if (side == EnumFacing.DOWN && tablet.player.getEyeHeight > 1) super.pushOrigin(side).offset(EnumFacing.DOWN) + else super.pushOrigin(side) + } + + class Rotatable(val rotatable: internal.Rotatable with EnvironmentHost) extends UpgradePiston(rotatable) { + override def pushDirection(args: Arguments, index: Int) = rotatable.toGlobal(args.optSideForAction(index, EnumFacing.SOUTH)) + } + +} \ No newline at end of file diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala index d8273c60e..b4001edec 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryControl.scala @@ -50,7 +50,7 @@ trait InventoryControl extends InventoryAware { @Callback(doc = "function(toSlot:number[, amount:number]):boolean -- Move up to the specified amount of items from the selected slot into the specified slot.") def transferTo(context: Context, args: Arguments): Array[AnyRef] = { val slot = args.checkSlot(inventory, 0) - val count = args.optionalItemCount(1) + val count = args.optItemCount(1) if (slot == selectedSlot || count == 0) { result(true) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala index f01f866d1..d0f08bbf5 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControl.scala @@ -33,7 +33,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest @Callback(doc = "function(side:number[, count:number=64]):boolean -- Drops items from the selected slot towards the specified side.") def drop(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - val count = args.optionalItemCount(1) + val count = args.optItemCount(1) val stack = inventory.getStackInSlot(selectedSlot) if (stack != null && stack.stackSize > 0) { val blockPos = position.offset(facing) @@ -69,7 +69,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest @Callback(doc = "function(side:number[, count:number=64]):boolean -- Suck up items from the specified side.") def suck(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - val count = args.optionalItemCount(1) + val count = args.optItemCount(1) val blockPos = position.offset(facing) if (InventoryUtils.inventoryAt(blockPos).exists(inventory => { diff --git a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala index 9822c2303..c4ac0b57f 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/InventoryWorldControlMk2.scala @@ -14,7 +14,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR @Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Drops the selected item stack into the specified slot of an inventory.""") def dropIntoSlot(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - val count = args.optionalItemCount(2) + val count = args.optItemCount(2) val stack = inventory.getStackInSlot(selectedSlot) if (stack != null && stack.stackSize > 0) { withInventory(facing, inventory => { @@ -43,7 +43,7 @@ trait InventoryWorldControlMk2 extends InventoryAware with WorldAware with SideR @Callback(doc = """function(facing:number, slot:number[, count:number]):boolean -- Sucks items from the specified slot of an inventory.""") def suckFromSlot(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - val count = args.optionalItemCount(2) + val count = args.optItemCount(2) withInventory(facing, inventory => { val slot = args.checkSlot(inventory, 1) if (InventoryUtils.extractFromInventorySlot(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, slot, count)) { diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala index a57293d32..33815f2e9 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankControl.scala @@ -53,7 +53,7 @@ trait TankControl extends TankAware { @Callback(doc = "function(index:number[, count:number=1000]):boolean -- Move the specified amount of fluid from the selected tank into the specified tank.") def transferFluidTo(context: Context, args: Arguments): Array[AnyRef] = { val index = args.checkTank(tank, 0) - val count = args.optionalFluidCount(1) + val count = args.optFluidCount(1) if (index == selectedTank || count == 0) { result(true) } diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala index dc90c8578..b54ca7e91 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankInventoryControl.scala @@ -35,7 +35,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware @Callback(doc = """function([amount:number]):boolean -- Transfers fluid from a tank in the selected inventory slot to the selected tank.""") def drain(context: Context, args: Arguments): Array[AnyRef] = { - val amount = args.optionalFluidCount(0) + val amount = args.optFluidCount(0) Option(tank.getFluidTank(selectedTank)) match { case Some(into) => inventory.getStackInSlot(selectedSlot) match { case stack: ItemStack => @@ -77,7 +77,7 @@ trait TankInventoryControl extends WorldAware with InventoryAware with TankAware @Callback(doc = """function([amount:number]):boolean -- Transfers fluid from the selected tank to a tank in the selected inventory slot.""") def fill(context: Context, args: Arguments): Array[AnyRef] = { - val amount = args.optionalFluidCount(0) + val amount = args.optFluidCount(0) Option(tank.getFluidTank(selectedTank)) match { case Some(from) => inventory.getStackInSlot(selectedSlot) match { case stack: ItemStack => diff --git a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala index 5024723bb..ee6254298 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/TankWorldControl.scala @@ -4,14 +4,9 @@ import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.util.ExtendedArguments._ -import li.cil.oc.util.ExtendedBlock._ -import li.cil.oc.util.ExtendedWorld._ +import li.cil.oc.util.FluidUtils import li.cil.oc.util.ResultWrapper.result -import net.minecraft.block.BlockLiquid -import net.minecraftforge.fluids.FluidRegistry import net.minecraftforge.fluids.FluidStack -import net.minecraftforge.fluids.IFluidBlock -import net.minecraftforge.fluids.IFluidHandler trait TankWorldControl extends TankAware with WorldAware with SideRestricted { @Callback(doc = "function(side:number):boolean -- Compare the fluid in the selected tank with the fluid on the specified side. Returns true if equal.") @@ -19,16 +14,10 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { val side = checkSideForAction(args, 0) fluidInTank(selectedTank) match { case Some(stack) => - val blockPos = position.offset(side) - if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match { - case handler: IFluidHandler => - result(Option(handler.getTankInfo(side.getOpposite)).exists(_.exists(other => stack.isFluidEqual(other.fluid)))) - case _ => - val block = world.getBlock(blockPos) - val fluid = FluidRegistry.lookupFluidForBlock(block) - result(stack.getFluid == fluid) + FluidUtils.fluidHandlerAt(position.offset(side)) match { + case Some(handler) => result(Option(handler.getTankInfo(side.getOpposite)).exists(_.exists(other => stack.isFluidEqual(other.fluid)))) + case _ => result(false) } - else result(false) case _ => result(false) } } @@ -36,18 +25,14 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { @Callback(doc = "function(side:boolean[, amount:number=1000]):boolean, number or string -- Drains the specified amount of fluid from the specified side. Returns the amount drained, or an error message.") def drain(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - val count = args.optionalFluidCount(1) + val count = args.optFluidCount(1) max 0 getTank(selectedTank) match { case Some(tank) => val space = tank.getCapacity - tank.getFluidAmount val amount = math.min(count, space) - if (count > 0 && amount == 0) { - result(Unit, "tank is full") - } - else { - val blockPos = position.offset(facing) - if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match { - case handler: IFluidHandler => + if (count < 1 || amount > 0) { + FluidUtils.fluidHandlerAt(position.offset(facing)) match { + case Some(handler) => tank.getFluid match { case stack: FluidStack => val drained = handler.drain(facing.getOpposite, new FluidStack(stack, amount), true) @@ -60,36 +45,10 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { val transferred = tank.fill(handler.drain(facing.getOpposite, amount, true), true) result(transferred > 0, transferred) } - case _ => world.getBlock(blockPos) match { - case fluidBlock: IFluidBlock if fluidBlock.canDrain(world, blockPos.toBlockPos) => - val drained = fluidBlock.drain(world, blockPos.toBlockPos, false) - if ((drained != null && drained.amount > 0) && (drained.amount <= amount || amount == 0)) { - if (drained.amount <= amount) { - val filled = tank.fill(fluidBlock.drain(world, blockPos.toBlockPos, true), true) - result(true, filled) - } - else /* if (amount == 0) */ { - result(true, 0) - } - } - else result(Unit, "tank is full") - case liquidBlock: BlockLiquid if world.getBlockState(blockPos.toBlockPos) == liquidBlock.getDefaultState => - val fluid = FluidRegistry.lookupFluidForBlock(liquidBlock) - if (fluid == null) { - result(Unit, "incompatible or no fluid") - } - else if (tank.fill(new FluidStack(fluid, 1000), false) == 1000) { - tank.fill(new FluidStack(fluid, 1000), true) - world.setBlockToAir(blockPos) - result(true, 1000) - } - else result(Unit, "tank is full") - case _ => - result(Unit, "incompatible or no fluid") - } + case _ => result(Unit, "incompatible or no fluid") } - else result(Unit, "incompatible or no fluid") } + else result(Unit, "tank is full") case _ => result(Unit, "no tank selected") } } @@ -97,49 +56,28 @@ trait TankWorldControl extends TankAware with WorldAware with SideRestricted { @Callback(doc = "function(side:number[, amount:number=1000]):boolean, number of string -- Eject the specified amount of fluid to the specified side. Returns the amount ejected or an error message.") def fill(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - val count = args.optionalFluidCount(1) + val count = args.optFluidCount(1) max 0 getTank(selectedTank) match { case Some(tank) => val amount = math.min(count, tank.getFluidAmount) - if (count > 0 && amount == 0) { - result(Unit, "tank is empty") + if (count < 1 || amount > 0) { + FluidUtils.fluidHandlerAt(position.offset(facing)) match { + case Some(handler) => + tank.getFluid match { + case stack: FluidStack => + val filled = handler.fill(facing.getOpposite, new FluidStack(stack, amount), true) + if (filled > 0 || amount == 0) { + tank.drain(filled, true) + result(true, filled) + } + else result(Unit, "incompatible or no fluid") + case _ => + result(Unit, "tank is empty") + } + case _ => result(Unit, "no space") + } } - val blockPos = position.offset(facing) - if (world.blockExists(blockPos)) world.getTileEntity(blockPos) match { - case handler: IFluidHandler => - tank.getFluid match { - case stack: FluidStack => - val filled = handler.fill(facing.getOpposite, new FluidStack(stack, amount), true) - if (filled > 0 || amount == 0) { - tank.drain(filled, true) - result(true, filled) - } - else result(Unit, "incompatible or no fluid") - case _ => - result(Unit, "tank is empty") - } - case _ => - val block = world.getBlock(blockPos) - if (!block.isAir(blockPos) && !block.isReplaceable(blockPos)) { - result(Unit, "no space") - } - else if (tank.getFluidAmount < 1000) { - result(Unit, "tank is empty") - } - else if (!tank.getFluid.getFluid.canBePlacedInWorld) { - result(Unit, "incompatible fluid") - } - else { - val fluidBlock = tank.getFluid.getFluid.getBlock - tank.drain(1000, true) - world.breakBlock(blockPos) - world.setBlock(blockPos, fluidBlock) - // This fake neighbor update is required to get stills to start flowing. - world.notifyBlockOfNeighborChange(blockPos, world.getBlock(position)) - result(true, 1000) - } - } - else result(Unit, "no space") + else result(Unit, "tank is empty") case _ => result(Unit, "no tank selected") } } diff --git a/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala b/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala index a0a863fd1..ec7950223 100644 --- a/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala +++ b/src/main/scala/li/cil/oc/server/component/traits/WorldTankAnalytics.scala @@ -5,15 +5,15 @@ import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.server.component.result -import li.cil.oc.util.ExtendedWorld._ -import net.minecraftforge.fluids.IFluidHandler +import li.cil.oc.util.FluidUtils trait WorldTankAnalytics extends WorldAware with SideRestricted { @Callback(doc = """function(side:number):number -- Get the amount of fluid in the tank on the specified side.""") def getTankLevel(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - world.getTileEntity(position.offset(facing)) match { - case handler: IFluidHandler => + + FluidUtils.fluidHandlerAt(position.offset(facing)) match { + case Some(handler) => result(handler.getTankInfo(facing.getOpposite).map(info => Option(info.fluid).fold(0)(_.amount)).sum) case _ => result(Unit, "no tank") } @@ -22,8 +22,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { @Callback(doc = """function(side:number):number -- Get the capacity of the tank on the specified side.""") def getTankCapacity(context: Context, args: Arguments): Array[AnyRef] = { val facing = checkSideForAction(args, 0) - world.getTileEntity(position.offset(facing)) match { - case handler: IFluidHandler => + FluidUtils.fluidHandlerAt(position.offset(facing)) match { + case Some(handler) => result(handler.getTankInfo(facing.getOpposite).map(_.capacity).foldLeft(0)((max, capacity) => math.max(max, capacity))) case _ => result(Unit, "no tank") } @@ -32,8 +32,8 @@ trait WorldTankAnalytics extends WorldAware with SideRestricted { @Callback(doc = """function(side:number):table -- Get a description of the fluid in the the tank on the specified side.""") def getFluidInTank(context: Context, args: Arguments): Array[AnyRef] = if (Settings.get.allowItemStackInspection) { val facing = checkSideForAction(args, 0) - world.getTileEntity(position.offset(facing)) match { - case handler: IFluidHandler => + FluidUtils.fluidHandlerAt(position.offset(facing)) match { + case Some(handler) => result(handler.getTankInfo(facing.getOpposite)) case _ => result(Unit, "no tank") } diff --git a/src/main/scala/li/cil/oc/server/machine/Machine.scala b/src/main/scala/li/cil/oc/server/machine/Machine.scala index ddaed36e8..71bc99d22 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -478,7 +478,7 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach // Check if we should switch states. These are all the states in which we're // guaranteed that the executor thread isn't running anymore. - state.synchronized(state.top match { + state.synchronized(state.top) match { // Booting up. case Machine.State.Starting => verifyComponents() @@ -539,10 +539,8 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach finally { inSynchronizedCall = false } - - assert(!isExecuting) case _ => // Nothing special to do, just avoid match errors. - }) + } // Finally check if we should stop the computer. We cannot lock the state // because we may have to wait for the executor thread to finish, which @@ -946,6 +944,8 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach case Machine.State.Stopping => state.clear() state.push(Machine.State.Stopping) + case Machine.State.Restarting => + // Nothing to do! case _ => throw new AssertionError("Invalid state in executor post-processing.") } assert(!isExecuting) diff --git a/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala index cf3b18f37..c92ea091b 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/ComputerAPI.scala @@ -1,9 +1,15 @@ package li.cil.oc.server.machine.luac import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.driver.item.MutableProcessor +import li.cil.oc.api.driver.item.Processor import li.cil.oc.api.network.Connector +import li.cil.oc.integration.opencomputers.DriverCPU import li.cil.oc.util.ExtendedLuaState.extendLuaState +import scala.collection.convert.WrapAsScala._ + class ComputerAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { def initialize() { // Computer API, stuff that kinda belongs to os, but we don't want to @@ -105,6 +111,51 @@ class ComputerAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { }) lua.setField(-2, "maxEnergy") + lua.pushScalaFunction(lua => { + machine.host.internalComponents.map(stack => (stack, api.Driver.driverFor(stack))).collectFirst { + case (stack, processor: MutableProcessor) => processor.allArchitectures.toSeq + case (stack, processor: Processor) => Seq(processor.architecture(stack)) + } match { + case Some(architectures) => + lua.pushValue(architectures.map(DriverCPU.getArchitectureName)) + case _ => + lua.newTable() + } + 1 + }) + lua.setField(-2, "getArchitectures") + + lua.pushScalaFunction(lua => { + machine.host.internalComponents.map(stack => (stack, api.Driver.driverFor(stack))).collectFirst { + case (stack, processor: Processor) => + lua.pushString(DriverCPU.getArchitectureName(processor.architecture(stack))) + 1 + }.getOrElse(0) + }) + lua.setField(-2, "getArchitecture") + + lua.pushScalaFunction(lua => { + val archName = lua.checkString(1) + machine.host.internalComponents.map(stack => (stack, api.Driver.driverFor(stack))).collectFirst { + case (stack, processor: MutableProcessor) => processor.allArchitectures.find(arch => DriverCPU.getArchitectureName(arch) == archName) match { + case Some(archClass) => + if (archClass != processor.architecture(stack)) { + processor.setArchitecture(stack, archClass) + lua.pushBoolean(true) + } + else { + lua.pushBoolean(false) + } + 1 + case _ => + lua.pushNil() + lua.pushString("unknown architecture") + 2 + } + }.getOrElse(0) + }) + lua.setField(-2, "setArchitecture") + // Set the computer table. lua.setGlobal("computer") } diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala b/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala index d27b826cb..cb87c1180 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/ComputerAPI.scala @@ -1,11 +1,17 @@ package li.cil.oc.server.machine.luaj import li.cil.oc.Settings +import li.cil.oc.api +import li.cil.oc.api.driver.item.MutableProcessor +import li.cil.oc.api.driver.item.Processor import li.cil.oc.api.network.Connector +import li.cil.oc.integration.opencomputers.DriverCPU import li.cil.oc.util.ScalaClosure._ import li.cil.repack.org.luaj.vm2.LuaValue import li.cil.repack.org.luaj.vm2.Varargs +import scala.collection.convert.WrapAsScala._ + class ComputerAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { override def initialize() { // Computer API, stuff that kinda belongs to os, but we don't want to @@ -54,6 +60,40 @@ class ComputerAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { computer.set("maxEnergy", (_: Varargs) => LuaValue.valueOf(node.asInstanceOf[Connector].globalBufferSize)) + computer.set("getArchitectures", (args: Varargs) => { + machine.host.internalComponents.map(stack => (stack, api.Driver.driverFor(stack))).collectFirst { + case (stack, processor: MutableProcessor) => processor.allArchitectures.toSeq + case (stack, processor: Processor) => Seq(processor.architecture(stack)) + } match { + case Some(architectures) => LuaValue.listOf(architectures.map(DriverCPU.getArchitectureName).map(LuaValue.valueOf).toArray) + case _ => LuaValue.tableOf() + } + }) + + computer.set("getArchitecture", (args: Varargs) => { + machine.host.internalComponents.map(stack => (stack, api.Driver.driverFor(stack))).collectFirst { + case (stack, processor: Processor) => LuaValue.valueOf(DriverCPU.getArchitectureName(processor.architecture(stack))) + }.getOrElse(LuaValue.NONE) + }) + + computer.set("setArchitecture", (args: Varargs) => { + val archName = args.checkjstring(1) + machine.host.internalComponents.map(stack => (stack, api.Driver.driverFor(stack))).collectFirst { + case (stack, processor: MutableProcessor) => processor.allArchitectures.find(arch => DriverCPU.getArchitectureName(arch) == archName) match { + case Some(archClass) => + if (archClass != processor.architecture(stack)) { + processor.setArchitecture(stack, archClass) + LuaValue.TRUE + } + else { + LuaValue.FALSE + } + case _ => + LuaValue.varargsOf(LuaValue.NIL, LuaValue.valueOf("unknown architecture")) + } + }.getOrElse(LuaValue.NONE) + }) + // Set the computer table. lua.set("computer", computer) } diff --git a/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala b/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala index ce56d5f01..ca6284836 100644 --- a/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala +++ b/src/main/scala/li/cil/oc/server/network/QuantumNetwork.scala @@ -1,20 +1,27 @@ package li.cil.oc.server.network -import li.cil.oc.server.component.LinkedCard +import li.cil.oc.api.network.Packet import scala.collection.mutable // Just because the name if so fancy! object QuantumNetwork { - val tunnels = mutable.Map.empty[String, mutable.WeakHashMap[LinkedCard, Unit]] + val tunnels = mutable.Map.empty[String, mutable.WeakHashMap[QuantumNode, Unit]] - def add(card: LinkedCard) { + def add(card: QuantumNode) { tunnels.getOrElseUpdate(card.tunnel, mutable.WeakHashMap.empty).put(card, Unit) } - def remove(card: LinkedCard) { + def remove(card: QuantumNode) { tunnels.get(card.tunnel).foreach(_.remove(card)) } - def getEndpoints(tunnel: String) = tunnels.get(tunnel).fold(Iterable.empty[LinkedCard])(_.keys) + def getEndpoints(tunnel: String) = tunnels.get(tunnel).fold(Iterable.empty[QuantumNode])(_.keys) + + trait QuantumNode { + def tunnel: String + + def receivePacket(packet: Packet): Unit + } + } diff --git a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala index 1bfbe6b86..eb6812c5f 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedArguments.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedArguments.scala @@ -4,6 +4,7 @@ import li.cil.oc.api.internal.MultiTank import li.cil.oc.api.machine.Arguments import net.minecraft.inventory.IInventory import net.minecraft.util.EnumFacing +import net.minecraftforge.fluids.FluidContainerRegistry import scala.language.implicitConversions @@ -12,17 +13,13 @@ object ExtendedArguments { implicit def extendedArguments(args: Arguments): ExtendedArguments = new ExtendedArguments(args) class ExtendedArguments(val args: Arguments) { - def optionalItemCount(n: Int) = - if (args.count > n && args.checkAny(n) != null) { - math.max(0, math.min(64, args.checkInteger(n))) - } - else 64 + def optItemCount(index: Int, default: Int = 64) = + if (!isDefined(index) || !hasValue(index)) default + else math.max(0, math.min(64, args.checkInteger(index))) - def optionalFluidCount(n: Int) = - if (args.count > n && args.checkAny(n) != null) { - math.max(0, args.checkInteger(n)) - } - else 1000 + def optFluidCount(index: Int, default: Int = FluidContainerRegistry.BUCKET_VOLUME) = + if (!isDefined(index) || !hasValue(index)) default + else math.max(0, args.checkInteger(index)) def checkSlot(inventory: IInventory, n: Int) = { val slot = args.checkInteger(n) - 1 @@ -32,9 +29,9 @@ object ExtendedArguments { slot } - def optSlot(inventory: IInventory, n: Int, default: Int) = { - if (n >= 0 && n < args.count()) checkSlot(inventory, n) - else default + def optSlot(inventory: IInventory, index: Int, default: Int) = { + if (!isDefined(index)) default + else checkSlot(inventory, index) } def checkTank(multi: MultiTank, n: Int) = { @@ -45,14 +42,26 @@ object ExtendedArguments { tank } - def checkSideForAction(n: Int) = checkSide(n, EnumFacing.SOUTH, EnumFacing.UP, EnumFacing.DOWN) + def checkSideForAction(index: Int) = checkSide(index, EnumFacing.SOUTH, EnumFacing.UP, EnumFacing.DOWN) - def checkSideForMovement(n: Int) = checkSide(n, EnumFacing.SOUTH, EnumFacing.NORTH, EnumFacing.UP, EnumFacing.DOWN) + def optSideForAction(index: Int, default: EnumFacing) = + if (!isDefined(index)) default + else checkSideForAction(index) - def checkSideForFace(n: Int, facing: EnumFacing) = checkSide(n, EnumFacing.values.filter(_ != facing.getOpposite): _*) + def checkSideForMovement(index: Int) = checkSide(index, EnumFacing.SOUTH, EnumFacing.NORTH, EnumFacing.UP, EnumFacing.DOWN) - def checkSide(n: Int, allowed: EnumFacing*) = { - val side = args.checkInteger(n) + def optSideForMovement(index: Int, default: EnumFacing) = + if (!isDefined(index)) default + else checkSideForMovement(index) + + def checkSideForFace(index: Int, facing: EnumFacing) = checkSide(index, EnumFacing.values.filter(_ != facing.getOpposite): _*) + + def optSideForFace(index: Int, default: EnumFacing) = + if (!isDefined(index)) default + else checkSideForAction(index) + + def checkSide(index: Int, allowed: EnumFacing*) = { + val side = args.checkInteger(index) if (side < 0 || side > 5) { throw new IllegalArgumentException("invalid side") } @@ -60,6 +69,15 @@ object ExtendedArguments { if (allowed.isEmpty || (allowed contains direction)) direction else throw new IllegalArgumentException("unsupported side") } + + def optSide(index: Int, default: EnumFacing, allowed: EnumFacing*) = { + if (!isDefined(index)) default + else checkSide(index, allowed: _*) + } + + private def isDefined(index: Int) = index >= 0 && index < args.count() + + private def hasValue(index: Int) = args.checkAny(index) != null } } diff --git a/src/main/scala/li/cil/oc/util/ExtendedBlock.scala b/src/main/scala/li/cil/oc/util/ExtendedBlock.scala index 158a3e007..77a991fe7 100644 --- a/src/main/scala/li/cil/oc/util/ExtendedBlock.scala +++ b/src/main/scala/li/cil/oc/util/ExtendedBlock.scala @@ -1,6 +1,7 @@ package li.cil.oc.util import net.minecraft.block.Block +import net.minecraftforge.fluids.IFluidBlock import scala.language.implicitConversions @@ -22,4 +23,14 @@ object ExtendedBlock { def getCollisionBoundingBoxFromPool(position: BlockPosition) = block.getCollisionBoundingBox(position.world.get, position.toBlockPos, position.world.get.getBlockState(position.toBlockPos)) } + implicit def extendedFluidBlock(block: IFluidBlock): ExtendedFluidBlock = new ExtendedFluidBlock(block) + + class ExtendedFluidBlock(val block: IFluidBlock) { + def drain(position: BlockPosition, doDrain: Boolean) = block.drain(position.world.get, position.toBlockPos, doDrain) + + def canDrain(position: BlockPosition) = block.canDrain(position.world.get, position.toBlockPos) + + def getFilledPercentage(position: BlockPosition) = block.getFilledPercentage(position.world.get, position.toBlockPos) + } + } diff --git a/src/main/scala/li/cil/oc/util/FluidUtils.scala b/src/main/scala/li/cil/oc/util/FluidUtils.scala new file mode 100644 index 000000000..1a58af63b --- /dev/null +++ b/src/main/scala/li/cil/oc/util/FluidUtils.scala @@ -0,0 +1,157 @@ +package li.cil.oc.util + +import li.cil.oc.util.ExtendedBlock._ +import li.cil.oc.util.ExtendedWorld._ +import net.minecraft.block.Block +import net.minecraft.block.BlockLiquid +import net.minecraft.util.EnumFacing +import net.minecraftforge.fluids.Fluid +import net.minecraftforge.fluids.FluidContainerRegistry +import net.minecraftforge.fluids.FluidRegistry +import net.minecraftforge.fluids.FluidStack +import net.minecraftforge.fluids.FluidTank +import net.minecraftforge.fluids.FluidTankInfo +import net.minecraftforge.fluids.IFluidBlock +import net.minecraftforge.fluids.IFluidHandler + +object FluidUtils { + /** + * Retrieves an actual fluid handler implementation for a specified world coordinate. + *

+ * This performs special handling for in-world liquids. + */ + def fluidHandlerAt(position: BlockPosition): Option[IFluidHandler] = position.world match { + case Some(world) if world.blockExists(position) => world.getTileEntity(position) match { + case handler: IFluidHandler => Option(handler) + case _ => Option(new GenericBlockWrapper(position)) + } + case _ => None + } + + /** + * Transfers some fluid between two fluid handlers. + *

+ * This will try to extract up the specified amount of fluid from any handler, + * then insert it into the specified sink handler. If the insertion fails, the + * fluid will remain in the source handler. + *

+ * This returns true if some fluid was transferred. + */ + def transferBetweenFluidHandlers(source: IFluidHandler, sourceSide: EnumFacing, sink: IFluidHandler, sinkSide: EnumFacing, limit: Int = FluidContainerRegistry.BUCKET_VOLUME) = { + val drained = source.drain(sourceSide, limit, false) + val filled = sink.fill(sinkSide, drained, false) + sink.fill(sinkSide, source.drain(sourceSide, filled, true), true) + } + + /** + * Utility method for calling transferBetweenFluidHandlers on handlers + * in the world. + *

+ * This uses the fluidHandlerAt method, and therefore handles special + * cases such as fluid blocks. + */ + def transferBetweenFluidHandlersAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sinkPos: BlockPosition, sinkSide: EnumFacing, limit: Int = FluidContainerRegistry.BUCKET_VOLUME) = + fluidHandlerAt(sourcePos).fold(0)(source => + fluidHandlerAt(sinkPos).fold(0)(sink => + transferBetweenFluidHandlers(source, sourceSide, sink, sinkSide, limit))) + + // ----------------------------------------------------------------------- // + + private class GenericBlockWrapper(position: BlockPosition) extends IFluidHandler { + override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = currentWrapper.fold(false)(_.canDrain(from, fluid)) + + override def drain(from: EnumFacing, resource: FluidStack, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(from, resource, doDrain)) + + override def drain(from: EnumFacing, maxDrain: Int, doDrain: Boolean): FluidStack = currentWrapper.fold(null: FluidStack)(_.drain(from, maxDrain, doDrain)) + + override def canFill(from: EnumFacing, fluid: Fluid): Boolean = currentWrapper.fold(false)(_.canFill(from, fluid)) + + override def fill(from: EnumFacing, resource: FluidStack, doFill: Boolean): Int = currentWrapper.fold(0)(_.fill(from, resource, doFill)) + + override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = currentWrapper.fold(Array.empty[FluidTankInfo])(_.getTankInfo(from)) + + def currentWrapper = if (position.world.get.blockExists(position)) position.world.get.getBlock(position) match { + case block: IFluidBlock => Option(new FluidBlockWrapper(position, block)) + case block: BlockLiquid if FluidRegistry.lookupFluidForBlock(block) != null => Option(new LiquidBlockWrapper(position, block)) + case block: Block if block.isAir(position) || block.isReplaceable(position) => Option(new AirBlockWrapper(position, block)) + case _ => None + } + else None + } + + private trait BlockWrapperBase extends IFluidHandler { + protected def uncheckedDrain(doDrain: Boolean): FluidStack + + override def drain(from: EnumFacing, resource: FluidStack, doDrain: Boolean): FluidStack = { + val drained = uncheckedDrain(false) + if (drained != null && (resource == null || (drained.getFluid == resource.getFluid && drained.amount <= resource.amount))) { + uncheckedDrain(doDrain) + } + else null + } + + override def drain(from: EnumFacing, maxDrain: Int, doDrain: Boolean): FluidStack = { + val drained = uncheckedDrain(false) + if (drained != null && drained.amount <= maxDrain) { + uncheckedDrain(doDrain) + } + else null + } + + override def canFill(from: EnumFacing, fluid: Fluid): Boolean = false + + override def fill(from: EnumFacing, resource: FluidStack, doFill: Boolean): Int = 0 + } + + private class FluidBlockWrapper(val position: BlockPosition, val block: IFluidBlock) extends BlockWrapperBase { + final val AssumedCapacity = FluidContainerRegistry.BUCKET_VOLUME + + override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = block.canDrain(position) + + override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = Array(new FluidTankInfo(new FluidTank(block.getFluid, (block.getFilledPercentage(position) * AssumedCapacity).toInt, AssumedCapacity))) + + override protected def uncheckedDrain(doDrain: Boolean): FluidStack = block.drain(position, doDrain) + } + + private class LiquidBlockWrapper(val position: BlockPosition, val block: BlockLiquid) extends BlockWrapperBase { + val fluid = FluidRegistry.lookupFluidForBlock(block) + + override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = true + + override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = Array(new FluidTankInfo(new FluidTank(fluid, FluidContainerRegistry.BUCKET_VOLUME, FluidContainerRegistry.BUCKET_VOLUME))) + + override protected def uncheckedDrain(doDrain: Boolean): FluidStack = { + if (doDrain) { + position.world.get.setBlockToAir(position) + } + new FluidStack(fluid, FluidContainerRegistry.BUCKET_VOLUME) + } + } + + private class AirBlockWrapper(val position: BlockPosition, val block: Block) extends IFluidHandler { + override def canDrain(from: EnumFacing, fluid: Fluid): Boolean = false + + override def drain(from: EnumFacing, resource: FluidStack, doDrain: Boolean): FluidStack = null + + override def drain(from: EnumFacing, maxDrain: Int, doDrain: Boolean): FluidStack = null + + override def canFill(from: EnumFacing, fluid: Fluid): Boolean = fluid.canBePlacedInWorld + + override def fill(from: EnumFacing, resource: FluidStack, doFill: Boolean): Int = { + if (resource != null && resource.getFluid.canBePlacedInWorld && resource.getFluid.getBlock != null) { + if (doFill) { + val world = position.world.get + world.breakBlock(position) + world.setBlock(position, resource.getFluid.getBlock) + // This fake neighbor update is required to get stills to start flowing. + world.notifyBlockOfNeighborChange(position, world.getBlock(position)) + } + FluidContainerRegistry.BUCKET_VOLUME + } + else 0 + } + + override def getTankInfo(from: EnumFacing): Array[FluidTankInfo] = Array.empty + } + +} diff --git a/src/main/scala/li/cil/oc/util/InventoryUtils.scala b/src/main/scala/li/cil/oc/util/InventoryUtils.scala index b27e2154e..ff2936839 100644 --- a/src/main/scala/li/cil/oc/util/InventoryUtils.scala +++ b/src/main/scala/li/cil/oc/util/InventoryUtils.scala @@ -155,7 +155,10 @@ object InventoryUtils { (stack != null && limit > 0) && { var success = false var remaining = limit - val range = slots.getOrElse(0 until inventory.getSizeInventory) + val range = slots.getOrElse(inventory match { + case sided: ISidedInventory => sided.getSlotsForFace(side.orNull).toIterable + case _ => 0 until inventory.getSizeInventory + }) if (range.nonEmpty) { // This is a special case for inserting with an explicit ordering, @@ -204,8 +207,13 @@ object InventoryUtils { *

* This returns true if at least one item was extracted. */ - def extractFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, limit: Int = 64) = - (0 until inventory.getSizeInventory).exists(slot => extractFromInventorySlot(consumer, inventory, side, slot, limit)) + def extractFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: EnumFacing, limit: Int = 64) = { + val range = inventory match { + case sided: ISidedInventory => sided.getSlotsForFace(side).toIterable + case _ => 0 until inventory.getSizeInventory + } + range.exists(slot => extractFromInventorySlot(consumer, inventory, side, slot, limit)) + } /** * Utility method for calling insertIntoInventory on an inventory @@ -221,6 +229,48 @@ object InventoryUtils { def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: EnumFacing, limit: Int = 64) = inventoryAt(position).exists(extractFromInventory(consumer, _, side, limit)) + /** + * Transfers some items between two inventories. + *

+ * This will try to extract up the specified number of items from any inventory, + * then insert it into the specified sink inventory. If the insertion fails, the + * items will remain in the source inventory. + *

+ * This uses the extractFromInventory and insertIntoInventory + * methods, and therefore handles special cases such as sided inventories and + * stack size limits. + *

+ * This returns true if at least one item was transferred. + */ + def transferBetweenInventories(source: IInventory, sourceSide: EnumFacing, sink: IInventory, sinkSide: Option[EnumFacing], limit: Int = 64) = + extractFromInventory( + insertIntoInventory(_, sink, sinkSide, limit), source, sourceSide, limit) + + /** + * Like transferBetweenInventories but moving between specific slots. + */ + def transferBetweenInventoriesSlots(source: IInventory, sourceSide: EnumFacing, sourceSlot: Int, sink: IInventory, sinkSide: Option[EnumFacing], sinkSlot: Int, limit: Int = 64) = + extractFromInventorySlot( + insertIntoInventorySlot(_, sink, sinkSide, sinkSlot, limit), source, sourceSide, sourceSlot, limit) + + /** + * Utility method for calling transferBetweenInventories on inventories + * in the world. + */ + def transferBetweenInventoriesAt(source: BlockPosition, sourceSide: EnumFacing, sink: BlockPosition, sinkSide: Option[EnumFacing], limit: Int = 64) = + inventoryAt(source).exists(sourceInventory => + inventoryAt(sink).exists(sinkInventory => + transferBetweenInventories(sourceInventory, sourceSide, sinkInventory, sinkSide, limit))) + + /** + * Utility method for calling transferBetweenInventoriesSlots on inventories + * in the world. + */ + def transferBetweenInventoriesSlotsAt(sourcePos: BlockPosition, sourceSide: EnumFacing, sourceSlot: Int, sinkPos: BlockPosition, sinkSide: Option[EnumFacing], sinkSlot: Int, limit: Int = 64) = + inventoryAt(sourcePos).exists(sourceInventory => + inventoryAt(sinkPos).exists(sinkInventory => + transferBetweenInventoriesSlots(sourceInventory, sourceSide, sourceSlot, sinkInventory, sinkSide, sinkSlot, limit))) + /** * Utility method for dropping contents from a single inventory slot into * the world. diff --git a/src/main/scala/li/cil/oc/util/RotationHelper.scala b/src/main/scala/li/cil/oc/util/RotationHelper.scala index b90710e1e..8eb8ee6e3 100644 --- a/src/main/scala/li/cil/oc/util/RotationHelper.scala +++ b/src/main/scala/li/cil/oc/util/RotationHelper.scala @@ -2,6 +2,8 @@ package li.cil.oc.util import net.minecraft.util.EnumFacing +import scala.collection.mutable + object RotationHelper { def fromYaw(yaw: Float) = { (yaw / 360 * 4).round & 3 match { @@ -11,4 +13,81 @@ object RotationHelper { case 3 => EnumFacing.EAST } } + + def toLocal(pitch: EnumFacing, yaw: EnumFacing, value: EnumFacing) = + translationFor(pitch, yaw)(value.ordinal) + + def toGlobal(pitch: EnumFacing, yaw: EnumFacing, value: EnumFacing) = + inverseTranslationFor(pitch, yaw)(value.ordinal) + + def translationFor(pitch: EnumFacing, yaw: EnumFacing) = + translationCache.synchronized(translationCache. + getOrElseUpdate(pitch, mutable.Map.empty). + getOrElseUpdate(yaw, translations(pitch.ordinal)(yaw.ordinal - 2))) + + def inverseTranslationFor(pitch: EnumFacing, yaw: EnumFacing) = + inverseTranslationCache.synchronized(inverseTranslationCache. + getOrElseUpdate(pitch, mutable.Map.empty). + getOrElseUpdate(yaw, { + val t = translationFor(pitch, yaw) + t.indices. + map(EnumFacing.getFront). + map(t.indexOf). + map(EnumFacing.getFront). + toArray + })) + + // ----------------------------------------------------------------------- // + + private val translationCache = mutable.Map.empty[EnumFacing, mutable.Map[EnumFacing, Array[EnumFacing]]] + private val inverseTranslationCache = mutable.Map.empty[EnumFacing, mutable.Map[EnumFacing, Array[EnumFacing]]] + + /** + * Translates forge directions based on the block's pitch and yaw. The base + * forward direction is facing south with no pitch. The outer array is for + * the three different pitch states, the inner for the four different yaw + * states. + */ + private val translations = Array( + // Pitch = Down + Array( + // Yaw = North + Array(D.south, D.north, D.up, D.down, D.east, D.west), + // Yaw = South + Array(D.south, D.north, D.down, D.up, D.west, D.east), + // Yaw = West + Array(D.south, D.north, D.west, D.east, D.up, D.down), + // Yaw = East + Array(D.south, D.north, D.east, D.west, D.down, D.up)), + // Pitch = Up + Array( + // Yaw = North + Array(D.north, D.south, D.down, D.up, D.east, D.west), + // Yaw = South + Array(D.north, D.south, D.up, D.down, D.west, D.east), + // Yaw = West + Array(D.north, D.south, D.west, D.east, D.down, D.up), + // Yaw = East + Array(D.north, D.south, D.east, D.west, D.up, D.down)), + // Pitch = Forward (North|East|South|West) + Array( + // Yaw = North + Array(D.down, D.up, D.south, D.north, D.east, D.west), + // Yaw = South + Array(D.down, D.up, D.north, D.south, D.west, D.east), + // Yaw = West + Array(D.down, D.up, D.west, D.east, D.south, D.north), + // Yaw = East + Array(D.down, D.up, D.east, D.west, D.north, D.south))) + + /** Shortcuts for forge directions to make the above more readable. */ + private object D { + val down = EnumFacing.DOWN + val up = EnumFacing.UP + val north = EnumFacing.NORTH + val south = EnumFacing.SOUTH + val west = EnumFacing.WEST + val east = EnumFacing.EAST + } + }