diff --git a/build.gradle b/build.gradle index 3c6beec91..3c19982de 100644 --- a/build.gradle +++ b/build.gradle @@ -83,10 +83,26 @@ repositories { name = "bc" url = "http://mod-buildcraft.com/" } + maven { + name = "BluePower" + url = "http://maven.bluepowermod.com/" + } + maven { + name = "chickenbones" + url = "http://chickenbones.net/maven/" + } maven { name = "ic2, forestry" url = "http://maven.ic2.player.to/" } + maven { + name = "IGW" + url = "http://maven.k-4u.nl/" + } + maven { + name = "mobius" + url = "http://mobiusstrip.eu/maven" + } maven { name = "ue" url = "http://calclavia.com/maven/" @@ -161,11 +177,15 @@ dependencies { provided "codechicken:EnderStorage:${config.minecraft.version}-${config.es.version}:dev" provided "codechicken:ForgeMultipart:${config.minecraft.version}-${config.fmp.version}:dev" provided "codechicken:WR-CBE:${config.minecraft.version}-${config.wrcbe.version}:dev" + provided "com.bluepowermod:BluePower:${config.bluepower.version}:deobf" provided "com.gregoriust.gregtech:gregtech_${config.minecraft.version}:${config.gt.version}:dev" provided "com.mod-buildcraft:buildcraft:${config.bc.version}:dev" provided "dev.calclavia.resonantengine:resonant-engine:${config.re.version}:dev" + provided "igwmod:IGW-Mod-1.7.10:${config.igwmod.version}:userdev" + provided "mcp.mobius.waila:Waila:${config.waila.version}_${config.minecraft.version}:dev" provided "net.industrial-craft:industrialcraft-2:${config.ic2.version}:dev" provided "net.sengir.forestry:forestry_${config.minecraft.version}:${config.forestry.version}:dev" + provided "qmunity:QmunityLib:${config.qmunitylib.version}:deobf" provided "tmech:TMechworks:${config.minecraft.version}-${config.tmech.version}:deobf" provided name: 'GalacticraftCoreAll', version: config.gc.version, ext: 'jar' @@ -226,7 +246,6 @@ minecraft { // stable_# stables are built at the discretion of the MCP team. mappings = "snapshot_20141130" - replaceIn "li/cil/oc/OpenComputers.scala" replace "@VERSION@", project.simpleVersion } diff --git a/build.properties b/build.properties index 6e9e4b5e6..07dc3a2db 100644 --- a/build.properties +++ b/build.properties @@ -8,6 +8,7 @@ ae2.version=rv2-beta-26 bc.version=6.4.9 bloodmagic.cf=2223/203 bloodmagic.version=1.3.0a-1 +bluepower.version=0.2.928 cc.cf=2216/236 cc.version=1.65 ccc.version=1.0.5.34 @@ -23,12 +24,14 @@ gc.build=3 gc.version=3.0.7 gt.version=5.04.06 ic2.version=2.2.654-experimental +igwmod.version=1.1.3-18 mekanism.build=5 mekanism.version=7.1.2 mfr.cf=2229/626 mfr.version=[1.7.10]2.8.0RC8-86 nei.version=1.0.5.82 projred.version=4.5.8.59 +qmunitylib.version=0.1.105 rc.cf=2219/321 rc.version=1.7.10-9.4.0.0 redlogic.version=59.0.3 diff --git a/src/main/java/li/cil/oc/api/API.java b/src/main/java/li/cil/oc/api/API.java index eb7f7c73f..1eeb3b042 100644 --- a/src/main/java/li/cil/oc/api/API.java +++ b/src/main/java/li/cil/oc/api/API.java @@ -1,11 +1,6 @@ package li.cil.oc.api; -import li.cil.oc.api.detail.DriverAPI; -import li.cil.oc.api.detail.FileSystemAPI; -import li.cil.oc.api.detail.ItemAPI; -import li.cil.oc.api.detail.MachineAPI; -import li.cil.oc.api.detail.ManualAPI; -import li.cil.oc.api.detail.NetworkAPI; +import li.cil.oc.api.detail.*; /** * Central reference for the API. @@ -16,7 +11,7 @@ import li.cil.oc.api.detail.NetworkAPI; */ public class API { public static final String ID_OWNER = "OpenComputers|Core"; - public static final String VERSION = "5.2.2"; + public static final String VERSION = "5.2.3"; public static DriverAPI driver = null; public static FileSystemAPI fileSystem = null; diff --git a/src/main/java/li/cil/oc/api/machine/Machine.java b/src/main/java/li/cil/oc/api/machine/Machine.java index 4b83405da..b7270f76c 100644 --- a/src/main/java/li/cil/oc/api/machine/Machine.java +++ b/src/main/java/li/cil/oc/api/machine/Machine.java @@ -141,6 +141,51 @@ public interface Machine extends ManagedEnvironment, Context { */ double cpuTime(); + // ----------------------------------------------------------------------- // + + /** + * Play a sound using the machine's built-in speaker. + *

+ * This is what's used to emit beep codes when an error occurs while trying + * to start the computer, for example, and what's used for playing sounds + * when computer.beep is called. + *

+ * Be responsible in how you limit calls to this, as each call will cause + * a packet to be sent to all nearby clients, and will cause the receiving + * clients to generate the required sound sample on-the-fly. It is + * therefore recommended to not call this too frequently, and to limit the + * length of the sound to something relatively short (not longer than a few + * seconds at most). + *

+ * The audio will be played at the machine's host's location. + * + * @param frequency the frequency of the tone to generate. + * @param duration the duration of the tone to generate, in milliseconds. + */ + void beep(short frequency, short duration); + + /** + * Utility method for playing beep codes. + *

+ * The underlying functionality is similar to that of {@link #beep(short, short)}, + * except that this will play tones at a fixed frequency, and two different + * durations - in a pattern as defined in the passed string. + *

+ * This is useful for generating beep codes, such as for boot errors. It + * has the advantage of only generating a single network packet, and + * generating a single, longer sound sample for the full pattern. As such + * the same considerations should be made as for {@link #beep(short, short)}, + * i.e. prefer not to use overly long patterns. + *

+ * The passed pattern must consist of dots (.) and dashes (-), + * where a dot is short tone, and a dash is a long tone. + *

+ * The audio will be played at the machine's host's location. + * + * @param pattern the beep pattern to play. + */ + void beep(String pattern); + /** * Crashes the computer. *

@@ -221,6 +266,8 @@ public interface Machine extends ManagedEnvironment, Context { */ Object[] invoke(Value value, String method, Object[] args) throws Exception; + // ----------------------------------------------------------------------- // + /** * The list of users registered on this machine. *

diff --git a/src/main/resources/assets/opencomputers/doc/en_US/block/print.md b/src/main/resources/assets/opencomputers/doc/en_US/block/print.md index 8fea03113..e1775103e 100644 --- a/src/main/resources/assets/opencomputers/doc/en_US/block/print.md +++ b/src/main/resources/assets/opencomputers/doc/en_US/block/print.md @@ -6,6 +6,6 @@ 3D prints can be recycled by putting them as input into a [3D printer](printer.md). This will re-use some of the [chamelium](../item/chamelium.md) that was used to print them. Color that was used to print the model will not be recycled. -Holding the key for OpenComputers' extended tooltips (default is [Shift]), a print's active state will be shown, if any. +Holding the key for OpenComputers' extended tooltips (default is `Shift`), a print's active state will be shown, if any. Printed blocks are also Forge MultiPart compatible. If present, multiple prints can be placed into a single block-space, unless they do not collide, and the total number of shapes in the block-space does not exceed the limit for a single model. Due to the nature of Forge MultiPart, prints can therefore also be placed into the same block-space as any other Forge MultiPart compatible block, such as torches, levers, cables or red alloy wires from Project Red, for example. 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 58779b515..744abec69 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 @@ -6,6 +6,6 @@ В дополнение к этому, точки доступа могут использоваться как повторители: они могут перенаправлять сообщения из проводной линии другим устройствам; или беспроводные сообщения как проводные, так и беспроводные. -Коммутаторы и [коммутаторы](switch.md) *не* отслеживают, какие пакеты и куда они передали, поэтому в сети могут образовываться петли или вы можете получать одно сообщение несколько раз. Из-за ограниченного буфера сообщений коммутатора, частое отправление сообщений приводит к их потере. Вы можете улучшить [коммутатор](switch.md) или точку доступа для увеличения скорости обработки сообщений, а также увеличения размера сообщений. +Точки доступа и [коммутаторы](switch.md) *не* отслеживают, какие пакеты и куда они передали, поэтому в сети могут образовываться петли или вы можете получать одно сообщение несколько раз. Из-за ограниченного буфера сообщений коммутатора, частое отправление сообщений приводит к их потере. Вы можете улучшить [коммутатор](switch.md) или точку доступа для увеличения скорости обработки сообщений, а также увеличения размера сообщений. Сообщения, могут перенаправлены всего несколько раз, поэтому цепочки с произвольным количеством коммутаторов или точек доступа невозможны. По умолчанию, сообщение может быть перенаправлено пять раз. diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/block/waypoint.md b/src/main/resources/assets/opencomputers/doc/ru_RU/block/waypoint.md new file mode 100644 index 000000000..a8922004e --- /dev/null +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/block/waypoint.md @@ -0,0 +1,9 @@ +# Путевая точка + +!["В этом направлении!" - "Нет, в этом!"](oredict:oc:waypoint) + +Главное не что это такое, а как это использовать. [Навигационное улучшение](../item/navigationUpgrade.md) может обнаруживать путевые точки, что позволяет устройствам с этим улучшением ориентироваться в игровом мире. Это можно использовать для написания программ для [роботов](robot.md) и [дронов](../item/drone.md). + +Обратите внимание, что актуальным местоположением считается *блок перед путевой точкой* (выделен с помощью частиц), именно это местоположение будет передано, при запросе его навигационным улучшением. Это позволяет поместить путевую точку рядом или над сундуком и позволит обратиться к путевой точке "над сундуком", без необходимости вращения самой точки. + +Путевая точка имеет два параметра, которые могут быть использованы при ее опросе навигационным улучшением: уровень редстоун сигнала, который получает точка и изменяемое имя. Имя это строка, состоящая из 32 символов, оно может быть изменено через интерфейс или через API. Эти два параметра могут быть использованы устройством, для определения, что можно сделать с путевой точкой. Например, сортировочная программа может использовать блоки с высоким уровнем редстоун сигнала как входные, а с низким как выходные. \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/doc/ru_RU/item/navigationUpgrade.md b/src/main/resources/assets/opencomputers/doc/ru_RU/item/navigationUpgrade.md index 3d8036c4c..88fd718c8 100644 --- a/src/main/resources/assets/opencomputers/doc/ru_RU/item/navigationUpgrade.md +++ b/src/main/resources/assets/opencomputers/doc/ru_RU/item/navigationUpgrade.md @@ -4,4 +4,6 @@ Данное улучшение добавляет навигацию и ориентацию для устройств. Получаемые координаты начинаются от центра карты, где было собрано улучшение, радиус функционирования зависит от размера карты. -Карта внутри улучшения может быть обновлена, повторным крафтом улучшения с новой картой. Старая карта при этом будет возвращена игроку. \ No newline at end of file +Карта внутри улучшения может быть обновлена, повторным крафтом улучшения с новой картой. Старая карта при этом будет возвращена игроку. + +Наиболее эффективно это улучшение работает в паре с одной или несколькими [точками доступа](../block/waypoint.md). \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lang/ru_RU.lang b/src/main/resources/assets/opencomputers/lang/ru_RU.lang index 00c7f09f4..4b66e4b7a 100644 --- a/src/main/resources/assets/opencomputers/lang/ru_RU.lang +++ b/src/main/resources/assets/opencomputers/lang/ru_RU.lang @@ -35,6 +35,7 @@ tile.oc.screen2.name=Монитор (2-ой уровень) tile.oc.screen3.name=Монитор (3-ий уровень) tile.oc.serverRack.name=Серверная стойка tile.oc.switch.name=Коммутатор +tile.oc.waypoint.name=Путевая точка # Items item.oc.AbstractBusCard.name=Карта абстрактной шины @@ -133,6 +134,7 @@ item.oc.UpgradeSolarGenerator.name=Улучшение "Солнечный ген item.oc.UpgradeTank.name=Улучшение "Бак для жидкостей" item.oc.UpgradeTankController.name=Улучшение "Контроллер бака" item.oc.UpgradeTractorBeam.name=Улучшение "Притягивающий луч" +oc:tooltip.Waypoint=Добавляет путевую точку для устройств с навигационным улучшением. item.oc.WirelessNetworkCard.name=Плата беспроводной сети item.oc.WorldSensorCard.name=Карта-мировой сенсор item.oc.wrench.name=Ключ diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/dmesg.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/dmesg.lua index ab3bb4a0e..077352930 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/bin/dmesg.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/bin/dmesg.lua @@ -2,15 +2,21 @@ local event = require "event" local component = require "component" local keyboard = require "keyboard" +local args = {...} + local interactive = io.output() == io.stdout local color, isPal, evt if interactive then color, isPal = component.gpu.getForeground() end -io.write("Press 'q' to exit\n") +io.write("Press 'Ctrl-C' to exit\n") pcall(function() repeat - evt = table.pack(event.pull()) + if #args > 0 then + evt = table.pack(event.pullMultiple("interrupted", table.unpack(args))) + else + evt = table.pack(event.pull()) + end if interactive then component.gpu.setForeground(0xCC2200) end io.write("[" .. os.date("%T") .. "] ") if interactive then component.gpu.setForeground(0x44CC00) end @@ -25,7 +31,7 @@ pcall(function() end io.write("\n") - until evt[1] == "key_down" and evt[4] == keyboard.keys.q + until evt[1] == "interrupted" end) if interactive then component.gpu.setForeground(color, isPal) diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/event.lua b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/event.lua index 76020d1e3..fa9cbfaaa 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/lib/event.lua +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/lib/event.lua @@ -4,19 +4,6 @@ local keyboard = require("keyboard") local event, listeners, timers = {}, {}, {} local lastInterrupt = -math.huge -local function matches(signal, name, filter) - if name and not (type(signal[1]) == "string" and signal[1]:match(name)) - then - return false - end - for i = 1, filter.n do - if filter[i] ~= nil and filter[i] ~= signal[i + 1] then - return false - end - end - return true -end - local function call(callback, ...) local result, message = pcall(callback, ...) if not result and type(event.onError) == "function" then @@ -64,6 +51,45 @@ local function tick() end end +local function createPlainFilter(name, ...) + local filter = table.pack(...) + if name == nil and filter.n == 0 then + return nil + end + + return function(...) + local signal = table.pack(...) + if name and not (type(signal[1]) == "string" and signal[1]:match(name)) then + return false + end + for i = 1, filter.n do + if filter[i] ~= nil and filter[i] ~= signal[i + 1] then + return false + end + end + return true + end +end + +local function createMultipleFilter(...) + local filter = table.pack(...) + if filter.n == 0 then + return nil + end + + return function(...) + local signal = table.pack(...) + if type(signal[1]) ~= "string" then + return false + end + for i = 1, filter.n do + if filter[i] ~= nil and signal[1]:match(filter[i]) then + return true + end + end + return false + end +end ------------------------------------------------------------------------------- function event.cancel(timerId) @@ -118,28 +144,50 @@ end function event.pull(...) local args = table.pack(...) - local seconds, name, filter if type(args[1]) == "string" then - name = args[1] - filter = table.pack(table.unpack(args, 2, args.n)) + return event.pullFiltered(createPlainFilter(...)) else checkArg(1, args[1], "number", "nil") checkArg(2, args[2], "string", "nil") - seconds = args[1] - name = args[2] - filter = table.pack(table.unpack(args, 3, args.n)) + return event.pullFiltered(args[1], createPlainFilter(select(2, ...))) end +end - local hasFilter = name ~= nil - if not hasFilter then - for i = 1, filter.n do - hasFilter = hasFilter or filter[i] ~= nil +function event.pullMultiple(...) + local seconds + local args + if type(...) == "number" then + seconds = ... + args = table.pack(select(2,...)) + for i=1,args.n do + checkArg(i+1, args[i], "string", "nil") end + else + args = table.pack(...) + for i=1,args.n do + checkArg(i, args[i], "string", "nil") + end + end + return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n))) + +end + +function event.pullFiltered(...) + local args = table.pack(...) + local seconds, filter + + if type(args[1]) == "function" then + filter = args[1] + else + checkArg(1, args[1], "number", "nil") + checkArg(2, args[2], "function", "nil") + seconds = args[1] + filter = args[2] end local deadline = seconds and (computer.uptime() + seconds) or - (hasFilter and math.huge or 0) + (filter and math.huge or 0) repeat local closest = seconds and deadline or math.huge for _, timer in pairs(timers) do @@ -154,9 +202,15 @@ function event.pull(...) lastInterrupt = computer.uptime() error("interrupted", 0) end - if not (seconds or hasFilter) or matches(signal, name, filter) then + if event.shouldSoftInterrupt() and (filter == nil or filter("interrupted", computer.uptime() - lastInterrupt)) then + local awaited = computer.uptime() - lastInterrupt + lastInterrupt = computer.uptime() + return "interrupted", awaited + end + if not (seconds or filter) or filter == nil or filter(table.unpack(signal, 1, signal.n)) then return table.unpack(signal, 1, signal.n) end + until computer.uptime() >= deadline end @@ -167,6 +221,12 @@ function event.shouldInterrupt() keyboard.isKeyDown(keyboard.keys.c) end +function event.shouldSoftInterrupt() + return computer.uptime() - lastInterrupt > 1 and + keyboard.isControlDown() and + keyboard.isKeyDown(keyboard.keys.c) +end + function event.timer(interval, callback, times) checkArg(1, interval, "number") checkArg(2, callback, "function") diff --git a/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/dmesg b/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/dmesg index 89f1f7e10..ee8f0d0bf 100644 --- a/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/dmesg +++ b/src/main/resources/assets/opencomputers/loot/OpenOS/usr/man/dmesg @@ -2,5 +2,12 @@ NAME dmesg - display messages(events) SYNOPIS - dmesg + dmesg [EVENT]... + +EXAMPLES + dmesg + Shows all events. + + dmesg touch + Shows only "touch" events. diff --git a/src/main/resources/assets/opencomputers/lua/machine.lua b/src/main/resources/assets/opencomputers/lua/machine.lua index e72d36f1f..27b90e6d8 100644 --- a/src/main/resources/assets/opencomputers/lua/machine.lua +++ b/src/main/resources/assets/opencomputers/lua/machine.lua @@ -888,6 +888,26 @@ sandbox = { }, debug = { + getinfo = function(...) + local result = debug.getinfo(...) + if result then + -- Only make primitive information available in the sandbox. + return { + source = result.source, + short_src = result.short_src, + linedefined = result.linedefined, + lastlinedefined = result.lastlinedefined, + what = result.what, + currentline = result.currentline, + nups = result.nups, + nparams = result.nparams, + isvararg = result.isvararg, + name = result.name, + namewhat = result.namewhat, + istailcall = result.istailcall + } + end + end, traceback = debug.traceback }, diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/MarkupFormat.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/MarkupFormat.scala new file mode 100644 index 000000000..72a5c3118 --- /dev/null +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/MarkupFormat.scala @@ -0,0 +1,5 @@ +package li.cil.oc.client.renderer.markdown + +object MarkupFormat extends Enumeration { + val Markdown, IGWMod = Value +} diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala index 7fd3e328f..d19a4d21e 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BasicTextSegment.scala @@ -1,6 +1,7 @@ package li.cil.oc.client.renderer.markdown.segment import li.cil.oc.client.renderer.markdown.Document +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.gui.FontRenderer trait BasicTextSegment extends Segment { @@ -38,6 +39,8 @@ trait BasicTextSegment extends Segment { lines * lineHeight(renderer) } + override def toString(format: MarkupFormat.Value): String = text + // ----------------------------------------------------------------------- // protected def text: String diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BoldSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BoldSegment.scala index 820d423b2..908bf155f 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BoldSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/BoldSegment.scala @@ -1,9 +1,13 @@ package li.cil.oc.client.renderer.markdown.segment +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.util.EnumChatFormatting private[markdown] class BoldSegment(parent: Segment, text: String) extends TextSegment(parent, text) { override protected def format = EnumChatFormatting.BOLD.toString - override def toString: String = s"{BoldSegment: text = $text}" + override def toString(format: MarkupFormat.Value): String = format match { + case MarkupFormat.Markdown => s"**$text**" + case MarkupFormat.IGWMod => s"[prefix{l}]$text [prefix{}]" + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala index 46f36f4aa..5356e55e4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/CodeSegment.scala @@ -1,6 +1,7 @@ package li.cil.oc.client.renderer.markdown.segment import li.cil.oc.client.renderer.TextBufferRenderCache +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.gui.FontRenderer import org.lwjgl.opengl.GL11 @@ -30,5 +31,8 @@ private[markdown] class CodeSegment(val parent: Segment, val text: String) exten override protected def stringWidth(s: String, renderer: FontRenderer): Int = s.length * TextBufferRenderCache.renderer.charRenderWidth - override def toString: String = s"{CodeSegment: text = $text}" + override def toString(format: MarkupFormat.Value): String = format match { + case MarkupFormat.Markdown => s"`$text`" + case MarkupFormat.IGWMod => s"[prefix{1}]$text [prefix{}]" + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/HeaderSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/HeaderSegment.scala index 18657a757..1baf4e06a 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/HeaderSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/HeaderSegment.scala @@ -1,5 +1,6 @@ package li.cil.oc.client.renderer.markdown.segment +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.util.EnumChatFormatting private[markdown] class HeaderSegment(parent: Segment, text: String, val level: Int) extends TextSegment(parent, text) { @@ -9,5 +10,8 @@ private[markdown] class HeaderSegment(parent: Segment, text: String, val level: override protected def format = EnumChatFormatting.UNDERLINE.toString - override def toString: String = s"{HeaderSegment: text = $text, level = $level}" + override def toString(format: MarkupFormat.Value): String = format match { + case MarkupFormat.Markdown => s"${"#" * level} $text" + case MarkupFormat.IGWMod => s"[prefix{l}]$text [prefix{}]" + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/ItalicSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/ItalicSegment.scala index ee6bd34d8..e8afb72e9 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/ItalicSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/ItalicSegment.scala @@ -1,9 +1,13 @@ package li.cil.oc.client.renderer.markdown.segment +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.util.EnumChatFormatting private[markdown] class ItalicSegment(parent: Segment, text: String) extends TextSegment(parent, text) { override protected def format = EnumChatFormatting.ITALIC.toString - override def toString: String = s"{ItalicSegment: text = $text}" + override def toString(format: MarkupFormat.Value): String = format match { + case MarkupFormat.Markdown => s"*$text*" + case MarkupFormat.IGWMod => s"[prefix{o}]$text [prefix{}]" + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala index 15fc30289..213cf4443 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/LinkSegment.scala @@ -3,8 +3,10 @@ package li.cil.oc.client.renderer.markdown.segment import java.net.URI import li.cil.oc.Localization +import li.cil.oc.OpenComputers import li.cil.oc.api import li.cil.oc.client.Manual +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.Minecraft private[markdown] class LinkSegment(parent: Segment, text: String, val url: String) extends TextSegment(parent, text) with InteractiveSegment { @@ -54,5 +56,10 @@ private[markdown] class LinkSegment(parent: Segment, text: String, val url: Stri } } - override def toString: String = s"{LinkSegment: text = $text, url = $url}" + override def toString(format: MarkupFormat.Value): String = format match { + case MarkupFormat.Markdown => s"[$text]($url)" + case MarkupFormat.IGWMod => + if (url.startsWith("http://") || url.startsWith("https://")) text + else s"[link{${OpenComputers.ID}:$url}]$text [link{}]" + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala index d1183338c..d635e98d5 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/RenderSegment.scala @@ -3,6 +3,7 @@ package li.cil.oc.client.renderer.markdown.segment import li.cil.oc.api.manual.ImageRenderer import li.cil.oc.api.manual.InteractiveImageRenderer import li.cil.oc.client.renderer.markdown.Document +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.gui.FontRenderer import org.lwjgl.opengl.GL11 @@ -74,5 +75,8 @@ private[markdown] class RenderSegment(val parent: Segment, val title: String, va hovered } - override def toString: String = s"{RendererSegment: title = $title, imageRenderer = $imageRenderer}" + override def toString(format: MarkupFormat.Value): String = format match { + case MarkupFormat.Markdown => s"![$title]($imageRenderer)" + case MarkupFormat.IGWMod => "(Sorry, images only work in the OpenComputers manual for now.)" // TODO + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala index 7e6ca0527..645dd1a90 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/Segment.scala @@ -1,8 +1,10 @@ package li.cil.oc.client.renderer.markdown.segment +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.client.gui.FontRenderer import scala.annotation.tailrec +import scala.collection.mutable import scala.util.matching.Regex trait Segment { @@ -46,6 +48,25 @@ trait Segment { */ def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = None + def renderAsText(format: MarkupFormat.Value): Iterable[String] = { + var segment = this + val result = mutable.Buffer.empty[String] + val builder = mutable.StringBuilder.newBuilder + while (segment != null) { + builder.append(segment.toString(format)) + if (segment.isLast) { + result += builder.toString() + builder.clear() + } + segment = segment.next + } + result.toIterable + } + + def toString(format: MarkupFormat.Value): String + + override def toString: String = toString(MarkupFormat.Markdown) + // ----------------------------------------------------------------------- // // Used during construction, checks a segment for inner segments. diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/StrikethroughSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/StrikethroughSegment.scala index 3b2056c79..e3b544e22 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/StrikethroughSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/StrikethroughSegment.scala @@ -1,9 +1,13 @@ package li.cil.oc.client.renderer.markdown.segment +import li.cil.oc.client.renderer.markdown.MarkupFormat import net.minecraft.util.EnumChatFormatting private[markdown] class StrikethroughSegment(parent: Segment, text: String) extends TextSegment(parent, text) { override protected def format = EnumChatFormatting.STRIKETHROUGH.toString - override def toString: String = s"{StrikethroughSegment: text = $text}" + override def toString(format: MarkupFormat.Value): String = format match { + case MarkupFormat.Markdown => s"~~$text~~" + case MarkupFormat.IGWMod => s"[prefix{m}]$text [prefix{}]" + } } diff --git a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala index 9c0bc8383..491c6faad 100644 --- a/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala +++ b/src/main/scala/li/cil/oc/client/renderer/markdown/segment/TextSegment.scala @@ -96,6 +96,4 @@ private[markdown] class TextSegment(val parent: Segment, val text: String) exten case _ => None } } - - override def toString: String = s"{TextSegment: text = $text}" } diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala index 97fc8e0ee..b9e980698 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala @@ -57,7 +57,18 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit /** Used to pass the current screen along to call(). */ private var hologram: Hologram = null + /** + * Whether initialization failed (e.g. due to an out of memory error) and we + * should render using the fallback renderer instead. + */ + private var failed = false + override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float, damage: Int) { + if (failed) { + HologramRendererFallback.renderTileEntityAt(tileEntity, x, y, z, f, damage) + return + } + RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)") hologram = tileEntity.asInstanceOf[Hologram] @@ -143,12 +154,13 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit } def draw(glBuffer: Int) { - initialize() - validate(glBuffer) - publish(glBuffer) + if (initialize()) { + validate(glBuffer) + publish(glBuffer) + } } - private def initialize() { + private def initialize(): Boolean = !failed && (try { // First run only, create structure information. if (commonBuffer == 0) { dataBuffer = BufferUtils.createIntBuffer(hologram.width * hologram.width * hologram.height * 6 * 4 * 2) @@ -223,7 +235,14 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, commonBuffer) GL15.glBufferData(GL15.GL_ARRAY_BUFFER, data, GL15.GL_STATIC_DRAW) } + true } + catch { + case oom: OutOfMemoryError => + HologramRendererFallback.text = "Not enough memory" + failed = true + false + }) private def validate(glBuffer: Int) { // Refresh indexes when the hologram's data changed. diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala index d5b627e6f..3cc20f7c5 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRendererFallback.scala @@ -7,7 +7,7 @@ import net.minecraft.tileentity.TileEntity import org.lwjgl.opengl.GL11 object HologramRendererFallback extends TileEntitySpecialRenderer { - val text = "Requires OpenGL 1.5" + var text = "Requires OpenGL 1.5" override def renderTileEntityAt(tileEntity: TileEntity, x: Double, y: Double, z: Double, f: Float, damage: Int) { RenderState.checkError(getClass.getName + ".renderTileEntityAt: entering (aka: wasntme)") diff --git a/src/main/scala/li/cil/oc/common/Loot.scala b/src/main/scala/li/cil/oc/common/Loot.scala index 581225c59..9e23d9a56 100644 --- a/src/main/scala/li/cil/oc/common/Loot.scala +++ b/src/main/scala/li/cil/oc/common/Loot.scala @@ -124,6 +124,6 @@ object Loot extends WeightedRandomChestContent(new ItemStack(null: Item), 1, 1, case Some(disk) => ChestGenHooks.generateStacks(random, disk, theMinimumChanceToGenerateItem, theMaximumChanceToGenerateItem) - case _ => Array.empty + case _ => Array.empty[ItemStack] } } diff --git a/src/main/scala/li/cil/oc/common/init/Items.scala b/src/main/scala/li/cil/oc/common/init/Items.scala index 798a90fb9..713262c74 100644 --- a/src/main/scala/li/cil/oc/common/init/Items.scala +++ b/src/main/scala/li/cil/oc/common/init/Items.scala @@ -31,9 +31,9 @@ import net.minecraftforge.fml.common.registry.GameRegistry import scala.collection.mutable object Items extends ItemAPI { - private val descriptors = mutable.Map.empty[String, ItemInfo] + val descriptors = mutable.Map.empty[String, ItemInfo] - private val names = mutable.Map.empty[Any, String] + val names = mutable.Map.empty[Any, String] override def get(name: String): ItemInfo = descriptors.get(name).orNull diff --git a/src/main/scala/li/cil/oc/common/item/Wrench.scala b/src/main/scala/li/cil/oc/common/item/Wrench.scala index 3360819f4..f35410815 100644 --- a/src/main/scala/li/cil/oc/common/item/Wrench.scala +++ b/src/main/scala/li/cil/oc/common/item/Wrench.scala @@ -16,6 +16,7 @@ import net.minecraft.world.World @Injectable.InterfaceList(Array( new Injectable.Interface(value = "appeng.api.implementations.items.IAEWrench", modid = Mods.IDs.AppliedEnergistics2), new Injectable.Interface(value = "buildcraft.api.tools.IToolWrench", modid = Mods.IDs.BuildCraftTools), + new Injectable.Interface(value = "com.bluepowermod.api.misc.IScrewdriver", modid = Mods.IDs.BluePower), new Injectable.Interface(value = "cofh.api.item.IToolHammer", modid = Mods.IDs.CoFHItem), new Injectable.Interface(value = "crazypants.enderio.tool.ITool", modid = Mods.IDs.EnderIO), new Injectable.Interface(value = "mekanism.api.IMekWrench", modid = Mods.IDs.Mekanism), @@ -49,6 +50,9 @@ class Wrench extends SimpleItem with api.internal.Wrench { def canWrench(stack: ItemStack, player: EntityPlayer, pos: BlockPos): Boolean = true + // BluePower + def damage(stack: ItemStack, damage: Int, player: EntityPlayer, simulated: Boolean): Boolean = damage == 0 + // BuildCraft def canWrench(player: EntityPlayer, pos: BlockPos): Boolean = true diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala index 91205f68a..a21915e78 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/BundledRedstoneAware.scala @@ -2,17 +2,16 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.Settings import li.cil.oc.integration.Mods +import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.util.ExtendedNBT._ import net.minecraftforge.fml.common.Optional /* TODO RedLogic import mods.immibis.redlogic.api.wiring.IBundledEmitter import mods.immibis.redlogic.api.wiring.IBundledUpdatable -import mods.immibis.redlogic.api.wiring.IInsulatedRedstoneWire */ /* TODO Project Red import mrtjp.projectred.api.IBundledTile -import mrtjp.projectred.api.ProjectRedAPI */ import net.minecraft.nbt.NBTTagCompound @@ -55,6 +54,23 @@ trait BundledRedstoneAware extends RedstoneAware /* with IBundledEmitter with IB def bundledInput(side: EnumFacing) = (_bundledInput(side.ordinal()), _rednetInput(side.ordinal())).zipped.map(math.max) + def bundledInput(side: EnumFacing, newBundledInput: Array[Int]): Unit = { + val ownBundledInput = _bundledInput(side.ordinal()) + val oldMaxValue = ownBundledInput.max + var changed = false + if (newBundledInput != null) for (color <- 0 until 16) { + changed = changed || (ownBundledInput(color) >= 0 && ownBundledInput(color) != newBundledInput(color)) + ownBundledInput(color) = newBundledInput(color) + } + else for (color <- 0 until 16) { + changed = changed || ownBundledInput(color) > 0 + ownBundledInput(color) = 0 + } + if (changed) { + onRedstoneInputChanged(side, oldMaxValue, ownBundledInput.max) + } + } + def bundledInput(side: EnumFacing, color: Int) = math.max(_bundledInput(side.ordinal())(color), _rednetInput(side.ordinal())(color)) @@ -92,21 +108,7 @@ trait BundledRedstoneAware extends RedstoneAware /* with IBundledEmitter with IB override def updateRedstoneInput(side: EnumFacing) { super.updateRedstoneInput(side) - val ownBundledInput = _bundledInput(side.ordinal()) - val newBundledInput = computeBundledInput(side) - val oldMaxValue = ownBundledInput.max - var changed = false - if (newBundledInput != null) for (color <- 0 until 16) { - changed = changed || (ownBundledInput(color) >= 0 && ownBundledInput(color) != newBundledInput(color)) - ownBundledInput(color) = newBundledInput(color) - } - else for (color <- 0 until 16) { - changed = changed || ownBundledInput(color) > 0 - ownBundledInput(color) = 0 - } - if (changed) { - onRedstoneInputChanged(side, oldMaxValue, ownBundledInput.max) - } + bundledInput(side, BundledRedstone.computeBundledInput(position, side)) } override def readFromNBTForServer(nbt: NBTTagCompound) { @@ -147,49 +149,6 @@ trait BundledRedstoneAware extends RedstoneAware /* with IBundledEmitter with IB // ----------------------------------------------------------------------- // - protected def computeBundledInput(side: EnumFacing): Array[Int] = { - /* TODO RedLogic - val redLogic = - if (Mods.RedLogic.isAvailable) { - val (nx, ny, nz) = (x + side.offsetX, y + side.offsetY, z + side.offsetZ) - if (world.blockExists(nx, ny, nz)) world.getTileEntity(nx, ny, nz) match { - case wire: IInsulatedRedstoneWire => - var strength: Array[Int] = null - for (face <- -1 to 5 if wire.wireConnectsInDirection(face, side.ordinal()) && strength == null) { - strength = Array.fill(16)(0) - strength(wire.getInsulatedWireColour) = wire.getEmittedSignalStrength(face, side.ordinal()) - } - strength - case emitter: IBundledEmitter => - var strength: Array[Int] = null - for (i <- -1 to 5 if strength == null) { - strength = Option(emitter.getBundledCableStrength(i, side.getOpposite.ordinal())).fold(null: Array[Int])(_.map(_ & 0xFF)) - } - strength - case _ => null - } - else null - } - else null - */ - /* TODO Project Red - val projectRed = - if (Mods.ProjectRedTransmission.isAvailable) { - Option(ProjectRedAPI.transmissionAPI.getBundledInput(world, x, y, z, side.ordinal)).fold(null: Array[Int])(_.map(_ & 0xFF)) - } - else null - */ - /* TODO RedLogic or Project Red - (redLogic, projectRed) match { - case (a: Array[Int], b: Array[Int]) => (a, b).zipped.map((r1, r2) => math.max(r1, r2)) - case (a: Array[Int], _) => a - case (_, b: Array[Int]) => b - case _ => null - } - */ - null - } - override protected def onRedstoneOutputEnabledChanged() { /* TODO MFR if (Mods.MineFactoryReloaded.isAvailable) { diff --git a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala index f20b7dcd7..c5be31e7f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/traits/RedstoneAware.scala @@ -2,10 +2,8 @@ package li.cil.oc.common.tileentity.traits import li.cil.oc.Settings import li.cil.oc.integration.Mods +import li.cil.oc.integration.util.BundledRedstone import li.cil.oc.server.{PacketSender => ServerPacketSender} -import li.cil.oc.util.BlockPosition -import li.cil.oc.util.ExtendedWorld._ -import net.minecraft.block.BlockRedstoneWire import net.minecraftforge.fml.common.Optional import net.minecraftforge.fml.relauncher.Side import net.minecraftforge.fml.relauncher.SideOnly @@ -17,7 +15,6 @@ import mods.immibis.redlogic.api.wiring.IRedstoneUpdatable import mods.immibis.redlogic.api.wiring.IWire */ -import net.minecraft.init.Blocks import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.EnumFacing @@ -52,6 +49,14 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi def input(side: EnumFacing) = _input(side.ordinal()) max 0 + def input(side: EnumFacing, newInput: Int): Unit = { + val oldInput = _input(side.ordinal()) + _input(side.ordinal()) = newInput + if (oldInput >= 0 && newInput != oldInput) { + onRedstoneInputChanged(side, oldInput, newInput) + } + } + def maxInput = EnumFacing.values.map(input).max def output(side: EnumFacing) = _output(toLocal(side).ordinal()) @@ -81,12 +86,7 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi } def updateRedstoneInput(side: EnumFacing) { - val oldInput = _input(side.ordinal()) - val newInput = computeInput(side) - _input(side.ordinal()) = newInput - if (oldInput >= 0 && newInput != oldInput) { - onRedstoneInputChanged(side, oldInput, newInput) - } + input(side, BundledRedstone.computeInput(position, side)) } // ----------------------------------------------------------------------- // @@ -122,32 +122,6 @@ trait RedstoneAware extends RotationAware /* with IConnectable with IRedstoneEmi // ----------------------------------------------------------------------- // - protected def computeInput(side: EnumFacing) = { - val blockPos = BlockPosition(x, y, z).offset(side) - if (!world.blockExists(blockPos)) 0 - else { - // See BlockRedstoneLogic.getInputStrength() for reference. - val vanilla = math.max(world.getIndirectPowerLevelTo(blockPos, side), - if (world.getBlock(blockPos) == Blocks.redstone_wire) world.getBlockMetadata(blockPos).getValue(BlockRedstoneWire.POWER).asInstanceOf[Integer].intValue() else 0) - val redLogic = 0 - /* TODO RedLogic - val redLogic = if (Mods.RedLogic.isAvailable) { - world.getTileEntity(blockPos) match { - case emitter: IRedstoneEmitter => - var strength = 0 - for (i <- -1 to 5) { - strength = math.max(strength, emitter.getEmittedSignalStrength(i, side.getOpposite.ordinal())) - } - strength - case _ => 0 - } - } - else 0 - */ - math.max(vanilla, redLogic) - } - } - protected def onRedstoneInputChanged(side: EnumFacing, oldMaxValue: Int, newMaxValue: Int) {} protected def onRedstoneOutputEnabledChanged() { diff --git a/src/main/scala/li/cil/oc/integration/Mods.scala b/src/main/scala/li/cil/oc/integration/Mods.scala index 53d97069f..ebabfac22 100644 --- a/src/main/scala/li/cil/oc/integration/Mods.scala +++ b/src/main/scala/li/cil/oc/integration/Mods.scala @@ -22,6 +22,7 @@ object Mods { val AppliedEnergistics2 = new SimpleMod(IDs.AppliedEnergistics2, version = "@[rv1,)", providesPower = true) val BattleGear2 = new SimpleMod(IDs.BattleGear2) val BloodMagic = new SimpleMod(IDs.BloodMagic) + val BluePower = new SimpleMod(IDs.BluePower) val BuildCraft = new SimpleMod(IDs.BuildCraft) val BuildCraftTiles = new SimpleMod(IDs.BuildCraftTiles) val BuildCraftTools = new SimpleMod(IDs.BuildCraftTools) @@ -43,6 +44,7 @@ object Mods { val GregTech = new ClassBasedMod(IDs.GregTech, "gregtech.api.GregTech_API")() val IndustrialCraft2 = new SimpleMod(IDs.IndustrialCraft2, providesPower = true) val IndustrialCraft2Classic = new SimpleMod(IDs.IndustrialCraft2Classic, providesPower = true) + val IngameWiki = new SimpleMod(IDs.IngameWiki, version = "@[1.1.3,)") val Mekanism = new SimpleMod(IDs.Mekanism, providesPower = true) val Minecraft = new SimpleMod(IDs.Minecraft) val MineFactoryReloaded = new SimpleMod(IDs.MineFactoryReloaded) @@ -76,43 +78,50 @@ object Mods { // ----------------------------------------------------------------------- // val Proxies = Array( - // integration.appeng.ModAppEng, - // integration.bloodmagic.ModBloodMagic, - // integration.buildcraft.tools.ModBuildCraftAPITools, - // integration.buildcraft.tiles.ModBuildCraftAPITiles, - // integration.buildcraft.transport.ModBuildCraftAPITransport, - // integration.cofh.energy.ModCoFHEnergy, - // integration.cofh.item.ModCoFHItem, - // integration.cofh.tileentity.ModCoFHTileEntity, - // integration.cofh.transport.ModCoFHTransport, - // integration.enderstorage.ModEnderStorage, - // integration.dsu.ModDeepStorageUnit, - // integration.forestry.ModForestry, - // integration.fmp.ModForgeMultipart, - // integration.gc.ModGalacticraft, - // integration.gregtech.ModGregtech, - // integration.ic2.ModIndustrialCraft2, - // integration.mfr.ModMineFactoryReloaded, - // integration.mystcraft.ModMystcraft, - // integration.railcraft.ModRailcraft, - // integration.stargatetech2.ModStargateTech2, - // integration.thaumcraft.ModThaumcraft, - // integration.thermalexpansion.ModThermalExpansion, - // integration.tcon.ModTinkersConstruct, - // integration.tmechworks.ModTMechworks, +// integration.appeng.ModAppEng, +// integration.bloodmagic.ModBloodMagic, +// integration.bluepower.ModBluePower, +// integration.buildcraft.tools.ModBuildCraftAPITools, +// integration.buildcraft.tiles.ModBuildCraftAPITiles, +// integration.buildcraft.transport.ModBuildCraftAPITransport, +// integration.cofh.energy.ModCoFHEnergy, +// integration.cofh.item.ModCoFHItem, +// integration.cofh.tileentity.ModCoFHTileEntity, +// integration.cofh.transport.ModCoFHTransport, +// integration.enderstorage.ModEnderStorage, +// integration.dsu.ModDeepStorageUnit, +// integration.forestry.ModForestry, +// integration.fmp.ModForgeMultipart, +// integration.gc.ModGalacticraft, +// integration.gregtech.ModGregtech, +// integration.ic2.ModIndustrialCraft2, +// integration.mfr.ModMineFactoryReloaded, +// integration.mystcraft.ModMystcraft, +// integration.projectred.ModProjectRed, +// integration.railcraft.ModRailcraft, +// integration.redlogic.ModRedLogic, +// integration.stargatetech2.ModStargateTech2, +// integration.thaumcraft.ModThaumcraft, +// integration.thermalexpansion.ModThermalExpansion, +// integration.tcon.ModTinkersConstruct, +// integration.tmechworks.ModTMechworks, integration.vanilla.ModVanilla, - // integration.versionchecker.ModVersionChecker, - // integration.waila.ModWaila, - // integration.wrcbe.ModWRCBE, - // integration.wrsve.ModWRSVE, + integration.versionchecker.ModVersionChecker, + integration.waila.ModWaila, +// integration.wrcbe.ModWRCBE, +// integration.wrsve.ModWRSVE, - // // Register the general IPeripheral driver last, if at all, to avoid it - // // being used rather than other more concrete implementations. - // integration.computercraft.ModComputerCraft, +// // Register the general IPeripheral driver last, if at all, to avoid it +// // being used rather than other more concrete implementations. +// integration.computercraft.ModComputerCraft, - // We go last to ensure all other mod integration is done, e.g. to + // We go late to ensure all other mod integration is done, e.g. to // allow properly checking if wireless redstone is present. integration.opencomputers.ModOpenComputers + + // Run IGW registration after OC registration because we use the manual + // in there to know which pages to register. +// integration.igw.ModIngameWiki ) def init(): Unit = { @@ -139,6 +148,7 @@ object Mods { final val AppliedEnergistics2 = "appliedenergistics2" final val BattleGear2 = "battlegear2" final val BloodMagic = "AWWayofTime" + final val BluePower = "bluepowerAPI" final val BuildCraft = "BuildCraft|Core" final val BuildCraftPower = "BuildCraftAPI|power" final val BuildCraftTiles = "BuildCraftAPI|tiles" @@ -161,6 +171,7 @@ object Mods { final val GregTech = "gregtech" final val IndustrialCraft2 = "IC2" final val IndustrialCraft2Classic = "IC2-Classic" + final val IngameWiki = "IGWMod" final val Mekanism = "Mekanism" final val Minecraft = "Minecraft" final val MineFactoryReloaded = "MineFactoryReloaded" diff --git a/src/main/scala/li/cil/oc/integration/bluepower/BundledRedstoneDevice.scala b/src/main/scala/li/cil/oc/integration/bluepower/BundledRedstoneDevice.scala new file mode 100644 index 000000000..2e20a0610 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bluepower/BundledRedstoneDevice.scala @@ -0,0 +1,38 @@ +package li.cil.oc.integration.bluepower + +import com.bluepowermod.api.BPApi +import com.bluepowermod.api.connect.ConnectionType +import com.bluepowermod.api.connect.IConnectionCache +import com.bluepowermod.api.misc.MinecraftColor +import com.bluepowermod.api.wire.redstone.IBundledDevice +import li.cil.oc.common.tileentity.traits.BundledRedstoneAware +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +class BundledRedstoneDevice(val tileEntity: BundledRedstoneAware) extends IBundledDevice { + lazy val cache = BPApi.getInstance.getRedstoneApi.createBundledConnectionCache(this) + + override def getX: Int = tileEntity.x + + override def getY: Int = tileEntity.y + + override def getZ: Int = tileEntity.z + + override def getWorld: World = tileEntity.world + + override def canConnect(side: ForgeDirection, dev: IBundledDevice, connectionType: ConnectionType): Boolean = tileEntity.isOutputEnabled + + override def isNormalFace(side: ForgeDirection): Boolean = true + + override def getBundledConnectionCache: IConnectionCache[_ <: IBundledDevice] = cache + + override def getBundledColor(side: ForgeDirection): MinecraftColor = MinecraftColor.ANY + + override def getBundledOutput(side: ForgeDirection): Array[Byte] = tileEntity.bundledOutput(side).map(_.toByte) + + override def getBundledPower(side: ForgeDirection): Array[Byte] = tileEntity.bundledInput(side).map(_.toByte) + + override def setBundledPower(side: ForgeDirection, power: Array[Byte]): Unit = tileEntity.bundledInput(side, power.map(_ & 0xFF)) + + override def onBundledUpdate(): Unit = tileEntity.checkRedstoneInputChanged() +} diff --git a/src/main/scala/li/cil/oc/integration/bluepower/ModBluePower.scala b/src/main/scala/li/cil/oc/integration/bluepower/ModBluePower.scala new file mode 100644 index 000000000..c97251e24 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bluepower/ModBluePower.scala @@ -0,0 +1,39 @@ +package li.cil.oc.integration.bluepower + +import com.bluepowermod.api.BPApi +import com.bluepowermod.api.wire.redstone.IBundledDevice +import com.bluepowermod.api.wire.redstone.IRedstoneDevice +import li.cil.oc.integration.ModProxy +import li.cil.oc.integration.Mods +import li.cil.oc.integration.util.BundledRedstone +import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider +import li.cil.oc.util.BlockPosition +import net.minecraftforge.common.util.ForgeDirection + +object ModBluePower extends ModProxy with RedstoneProvider { + override def getMod = Mods.BluePower + + override def initialize(): Unit = { + RedstoneProvider.init() + + BundledRedstone.addProvider(this) + } + + override def computeInput(pos: BlockPosition, side: ForgeDirection): Int = { + val world = pos.world.get + val (nx, ny, nz) = (pos.x + side.offsetX, pos.y + side.offsetY, pos.z + side.offsetZ) + ForgeDirection.values.map(BPApi.getInstance.getRedstoneApi.getRedstoneDevice(world, nx, ny, nz, _, ForgeDirection.UNKNOWN)).collect { + case device: IRedstoneDevice => device.getRedstonePower(side.getOpposite) & 0xFF + }.padTo(1, 0).max + } + + def computeBundledInput(pos: BlockPosition, side: ForgeDirection): Array[Int] = { + val world = pos.world.get + val (nx, ny, nz) = (pos.x + side.offsetX, pos.y + side.offsetY, pos.z + side.offsetZ) + val inputs = ForgeDirection.values.map(BPApi.getInstance.getRedstoneApi.getBundledDevice(world, nx, ny, nz, _, ForgeDirection.UNKNOWN)).collect { + case device: IBundledDevice => Option(device.getBundledOutput(side.getOpposite)).fold(null: Array[Int])(_.map(_ & 0xFF)) + }.filter(_ != null) + if (inputs.isEmpty) null + else inputs.reduce((a, b) => (a, b).zipped.map((l, r) => math.max(l, r))) + } +} diff --git a/src/main/scala/li/cil/oc/integration/bluepower/RedstoneDevice.scala b/src/main/scala/li/cil/oc/integration/bluepower/RedstoneDevice.scala new file mode 100644 index 000000000..f97193757 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bluepower/RedstoneDevice.scala @@ -0,0 +1,33 @@ +package li.cil.oc.integration.bluepower + +import com.bluepowermod.api.BPApi +import com.bluepowermod.api.connect.ConnectionType +import com.bluepowermod.api.connect.IConnectionCache +import com.bluepowermod.api.wire.redstone.IRedstoneDevice +import li.cil.oc.common.tileentity.traits.RedstoneAware +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +class RedstoneDevice(val tileEntity: RedstoneAware) extends IRedstoneDevice { + lazy val cache = BPApi.getInstance.getRedstoneApi.createRedstoneConnectionCache(this) + + override def getX: Int = tileEntity.x + + override def getY: Int = tileEntity.y + + override def getZ: Int = tileEntity.z + + override def getWorld: World = tileEntity.world + + override def canConnect(side: ForgeDirection, dev: IRedstoneDevice, connectionType: ConnectionType): Boolean = tileEntity.isOutputEnabled + + override def isNormalFace(side: ForgeDirection): Boolean = true + + override def getRedstoneConnectionCache: IConnectionCache[_ <: IRedstoneDevice] = cache + + override def getRedstonePower(side: ForgeDirection): Byte = tileEntity.output(side).toByte + + override def setRedstonePower(side: ForgeDirection, power: Byte): Unit = tileEntity.input(side, power & 0xFF) + + override def onRedstoneUpdate(): Unit = tileEntity.checkRedstoneInputChanged() +} diff --git a/src/main/scala/li/cil/oc/integration/bluepower/RedstoneProvider.scala b/src/main/scala/li/cil/oc/integration/bluepower/RedstoneProvider.scala new file mode 100644 index 000000000..489ff09d1 --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/bluepower/RedstoneProvider.scala @@ -0,0 +1,36 @@ +package li.cil.oc.integration.bluepower + +import com.bluepowermod.api.BPApi +import com.bluepowermod.api.wire.redstone.IBundledDevice +import com.bluepowermod.api.wire.redstone.IRedstoneDevice +import com.bluepowermod.api.wire.redstone.IRedstoneProvider +import li.cil.oc.common.tileentity.traits.BundledRedstoneAware +import li.cil.oc.common.tileentity.traits.RedstoneAware +import net.minecraft.world.World +import net.minecraftforge.common.util.ForgeDirection + +import scala.collection.mutable + +object RedstoneProvider extends IRedstoneProvider { + val redstoneDevices = mutable.WeakHashMap.empty[RedstoneAware, RedstoneDevice] + + val bundledRedstoneDevices = mutable.WeakHashMap.empty[BundledRedstoneAware, BundledRedstoneDevice] + + def init(): Unit = { + BPApi.getInstance.getRedstoneApi.registerRedstoneProvider(this) + } + + override def getRedstoneDeviceAt(world: World, x: Int, y: Int, z: Int, side: ForgeDirection, face: ForgeDirection): IRedstoneDevice = { + world.getTileEntity(x, y, z) match { + case tileEntity: RedstoneAware => redstoneDevices.getOrElseUpdate(tileEntity, new RedstoneDevice(tileEntity)) + case _ => null + } + } + + override def getBundledDeviceAt(world: World, x: Int, y: Int, z: Int, side: ForgeDirection, face: ForgeDirection): IBundledDevice = { + world.getTileEntity(x, y, z) match { + case tileEntity: BundledRedstoneAware => bundledRedstoneDevices.getOrElseUpdate(tileEntity, new BundledRedstoneDevice(tileEntity)) + case _ => null + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/igw/ModIngameWiki.scala b/src/main/scala/li/cil/oc/integration/igw/ModIngameWiki.scala new file mode 100644 index 000000000..2e85980ca --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/igw/ModIngameWiki.scala @@ -0,0 +1,14 @@ +package li.cil.oc.integration.igw + +import li.cil.oc.api.Driver +import li.cil.oc.integration.ModProxy +import li.cil.oc.integration.Mods +import net.minecraftforge.common.MinecraftForge + +object ModIngameWiki extends ModProxy { + override def getMod = Mods.IngameWiki + + override def initialize(): Unit = { + WikiEventHandler.init() + } +} diff --git a/src/main/scala/li/cil/oc/integration/igw/WikiEventHandler.scala b/src/main/scala/li/cil/oc/integration/igw/WikiEventHandler.scala new file mode 100644 index 000000000..c8b8dfe1d --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/igw/WikiEventHandler.scala @@ -0,0 +1,57 @@ +package li.cil.oc.integration.igw + +import java.util + +import cpw.mods.fml.common.eventhandler.SubscribeEvent +import igwmod.api.PageChangeEvent +import igwmod.api.WikiRegistry +import li.cil.oc.OpenComputers +import li.cil.oc.api +import li.cil.oc.client.Manual +import li.cil.oc.client.renderer.markdown +import li.cil.oc.client.renderer.markdown.MarkupFormat +import li.cil.oc.common.init.Items +import net.minecraftforge.common.MinecraftForge + +import scala.collection.convert.WrapAsJava._ +import scala.collection.convert.WrapAsScala._ + +object WikiEventHandler { + var lastPath = "" + + def init(): Unit = { + MinecraftForge.EVENT_BUS.register(this) + + for ((name, info) <- Items.descriptors) { + val stack = info.createItemStack(1) + val path = api.Manual.pathFor(stack) + if (path != null && api.Manual.contentFor(path) != null) { + WikiRegistry.registerBlockAndItemPageEntry(stack) + } + } + } + + @SubscribeEvent + def onPageChangeEvent(e: PageChangeEvent): Unit = { + val path = + if (e.associatedStack != null) + "/" + api.Manual.pathFor(e.associatedStack) + else if (e.currentFile.startsWith(OpenComputers.ID + ":")) + e.currentFile.stripPrefix(OpenComputers.ID + ":") + else null + + val base = lastPath + lastPath = "" + if (path != null) { + val resolvedPath = Manual.makeRelative(path, base) + val content = api.Manual.contentFor(resolvedPath) + if (content != null) { + val document = markdown.Document.parse(content) + val processed = document.renderAsText(MarkupFormat.IGWMod) + e.pageText = new util.ArrayList[String](asJavaCollection(processed)) + e.currentFile = resolvedPath + lastPath = resolvedPath + } + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala b/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala new file mode 100644 index 000000000..b2982427a --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/projectred/ModProjectRed.scala @@ -0,0 +1,24 @@ +package li.cil.oc.integration.projectred + +import li.cil.oc.integration.ModProxy +import li.cil.oc.integration.Mods +import li.cil.oc.integration.util.BundledRedstone +import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider +import li.cil.oc.util.BlockPosition +import mrtjp.projectred.api.ProjectRedAPI +import net.minecraftforge.common.util.ForgeDirection + +object ModProjectRed extends ModProxy with RedstoneProvider { + override def getMod = Mods.ProjectRedTransmission + + override def initialize(): Unit = { + BundledRedstone.addProvider(this) + } + + override def computeInput(pos: BlockPosition, side: ForgeDirection): Int = 0 + + def computeBundledInput(pos: BlockPosition, side: ForgeDirection): Array[Int] = { + Option(ProjectRedAPI.transmissionAPI.getBundledInput(pos.world.get, pos.x, pos.y, pos.z, side.ordinal)). + fold(null: Array[Int])(_.map(_ & 0xFF)) + } +} diff --git a/src/main/scala/li/cil/oc/integration/redlogic/ModRedLogic.scala b/src/main/scala/li/cil/oc/integration/redlogic/ModRedLogic.scala new file mode 100644 index 000000000..a762a2f5b --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/redlogic/ModRedLogic.scala @@ -0,0 +1,53 @@ +package li.cil.oc.integration.redlogic + +import li.cil.oc.integration.ModProxy +import li.cil.oc.integration.Mods +import li.cil.oc.integration.util.BundledRedstone +import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider +import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedWorld._ +import mods.immibis.redlogic.api.wiring.IBundledEmitter +import mods.immibis.redlogic.api.wiring.IInsulatedRedstoneWire +import mods.immibis.redlogic.api.wiring.IRedstoneEmitter +import net.minecraftforge.common.util.ForgeDirection + +object ModRedLogic extends ModProxy with RedstoneProvider { + override def getMod = Mods.RedLogic + + override def initialize(): Unit = { + BundledRedstone.addProvider(this) + } + + override def computeInput(pos: BlockPosition, side: ForgeDirection): Int = { + pos.world.get.getTileEntity(pos) match { + case emitter: IRedstoneEmitter => + var strength = 0 + for (i <- -1 to 5) { + strength = math.max(strength, emitter.getEmittedSignalStrength(i, side.getOpposite.ordinal())) + } + strength + case _ => 0 + } + } + + def computeBundledInput(pos: BlockPosition, side: ForgeDirection): Array[Int] = { + val world = pos.world.get + val npos = pos.offset(side) + world.getTileEntity(npos) match { + case wire: IInsulatedRedstoneWire => + var strength: Array[Int] = null + for (face <- -1 to 5 if wire.wireConnectsInDirection(face, side.ordinal())) { + if (strength == null) strength = Array.fill(16)(0) + strength(wire.getInsulatedWireColour) = math.max(strength(wire.getInsulatedWireColour), wire.getEmittedSignalStrength(face, side.ordinal())) + } + strength + case emitter: IBundledEmitter => + var strength: Array[Int] = null + for (i <- -1 to 5 if strength == null) { + strength = Option(emitter.getBundledCableStrength(i, side.getOpposite.ordinal())).fold(null: Array[Int])(_.map(_ & 0xFF)) + } + strength + case _ => null + } + } +} diff --git a/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala b/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala index 2502881dc..847f6f695 100644 --- a/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala +++ b/src/main/scala/li/cil/oc/integration/util/BundledRedstone.scala @@ -1,9 +1,38 @@ package li.cil.oc.integration.util import li.cil.oc.integration.Mods +import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedWorld._ +import net.minecraft.util.EnumFacing + +import scala.collection.mutable object BundledRedstone { - def isAvailable = Mods.RedLogic.isAvailable || - Mods.MineFactoryReloaded.isAvailable || - Mods.ProjectRedTransmission.isAvailable + val providers = mutable.Buffer.empty[RedstoneProvider] + + def addProvider(provider: RedstoneProvider): Unit = providers += provider + + def isAvailable = Mods.MineFactoryReloaded.isAvailable || providers.length > 0 + + def computeInput(pos: BlockPosition, side: EnumFacing): Int = { + if (pos.world.get.blockExists(pos.offset(side))) + providers.map(_.computeInput(pos, side)).padTo(1, 0).max + else 0 + } + + def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] = { + if (pos.world.get.blockExists(pos.offset(side))) { + val inputs = providers.map(_.computeBundledInput(pos, side)).filter(_ != null) + if (inputs.isEmpty) null + else inputs.reduce((a, b) => (a, b).zipped.map((l, r) => math.max(l, r))) + } + else null + } + + trait RedstoneProvider { + def computeInput(pos: BlockPosition, side: EnumFacing): Int + + def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] + } + } 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 a1916d1be..958bbee3e 100644 --- a/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala +++ b/src/main/scala/li/cil/oc/integration/vanilla/ModVanilla.scala @@ -4,8 +4,15 @@ import li.cil.oc.Settings import li.cil.oc.api.Driver import li.cil.oc.integration.ModProxy import li.cil.oc.integration.Mods +import li.cil.oc.integration.util.BundledRedstone +import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider +import li.cil.oc.util.BlockPosition +import li.cil.oc.util.ExtendedWorld._ +import net.minecraft.block.BlockRedstoneWire +import net.minecraft.init.Blocks +import net.minecraft.util.EnumFacing -object ModVanilla extends ModProxy { +object ModVanilla extends ModProxy with RedstoneProvider { def getMod = Mods.Minecraft def initialize() { @@ -36,5 +43,16 @@ object ModVanilla extends ModProxy { Driver.add(ConverterWorldProvider) RecipeHandler.init() + + BundledRedstone.addProvider(this) } + + override def computeInput(pos: BlockPosition, side: EnumFacing): Int = { + val world = pos.world.get + // See BlockRedstoneLogic.getInputStrength() for reference. + math.max(world.getIndirectPowerLevelTo(pos, side), + if (world.getBlock(pos) == Blocks.redstone_wire) world.getBlockMetadata(pos).getValue(BlockRedstoneWire.POWER).asInstanceOf[Integer].intValue() else 0) + } + + override def computeBundledInput(pos: BlockPosition, side: EnumFacing): Array[Int] = null } diff --git a/src/main/scala/li/cil/oc/server/component/EEPROM.scala b/src/main/scala/li/cil/oc/server/component/EEPROM.scala index 5be937efb..eb1330638 100644 --- a/src/main/scala/li/cil/oc/server/component/EEPROM.scala +++ b/src/main/scala/li/cil/oc/server/component/EEPROM.scala @@ -39,7 +39,7 @@ class EEPROM extends prefab.ManagedEnvironment { if (!node.tryChangeBuffer(-Settings.get.eepromWriteCost)) { return result(Unit, "not enough energy") } - val newData = args.optByteArray(0, Array.empty) + val newData = args.optByteArray(0, Array.empty[Byte]) if (newData.length > Settings.get.eepromSize) throw new IllegalArgumentException("not enough space") codeData = newData context.pause(2) // deliberately slow to discourage use as normal storage medium @@ -85,7 +85,7 @@ class EEPROM extends prefab.ManagedEnvironment { if (!node.tryChangeBuffer(-Settings.get.eepromWriteCost)) { return result(Unit, "not enough energy") } - val newData = args.optByteArray(0, Array.empty) + val newData = args.optByteArray(0, Array.empty[Byte]) if (newData.length > Settings.get.eepromDataSize) throw new IllegalArgumentException("not enough space") volatileData = newData context.pause(1) // deliberately slow to discourage use as normal storage medium 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 5af7f3296..e50935ed9 100644 --- a/src/main/scala/li/cil/oc/server/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/machine/Machine.scala @@ -235,10 +235,6 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach false }) - private def beep(pattern: String) { - PacketSender.sendSound(host.world, host.xPosition, host.yPosition, host.zPosition, pattern) - } - override def pause(seconds: Double): Boolean = { val ticksToPause = math.max((seconds * 20).toInt, 0) def shouldPause(state: Machine.State.Value) = state match { @@ -270,6 +266,14 @@ class Machine(val host: MachineHost) extends prefab.ManagedEnvironment with mach true }) + override def beep(frequency: Short, duration: Short): Unit = { + PacketSender.sendSound(host.world, host.xPosition, host.yPosition, host.zPosition, frequency, duration) + } + + override def beep(pattern: String) { + PacketSender.sendSound(host.world, host.xPosition, host.yPosition, host.zPosition, pattern) + } + override def crash(message: String) = { this.message = Option(message) state.synchronized { diff --git a/src/main/scala/li/cil/oc/util/Audio.scala b/src/main/scala/li/cil/oc/util/Audio.scala index 813186433..1a8b645c2 100644 --- a/src/main/scala/li/cil/oc/util/Audio.scala +++ b/src/main/scala/li/cil/oc/util/Audio.scala @@ -5,7 +5,9 @@ import java.nio.ByteBuffer import li.cil.oc.OpenComputers import li.cil.oc.Settings import net.minecraft.client.Minecraft +import net.minecraft.client.audio.PositionedSoundRecord import net.minecraft.client.audio.SoundCategory +import net.minecraft.util.ResourceLocation import net.minecraftforge.fml.common.FMLCommonHandler import net.minecraftforge.fml.common.eventhandler.SubscribeEvent import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent @@ -39,10 +41,29 @@ object Audio { } def play(x: Float, y: Float, z: Float, pattern: String, frequencyInHz: Int = 1000, durationInMilliseconds: Int = 200): Unit = { - if (!disableAudio) { - val distanceBasedGain = math.max(0, 1 - Minecraft.getMinecraft.thePlayer.getDistance(x, y, z) / 12).toFloat - val gain = distanceBasedGain * volume - if (gain > 0 && amplitude > 0 && AL.isCreated) { + val mc = Minecraft.getMinecraft + val distanceBasedGain = math.max(0, 1 - mc.thePlayer.getDistance(x, y, z) / 12).toFloat + val gain = distanceBasedGain * volume + if (gain <= 0 || amplitude <= 0) return + + if (disableAudio) { + // Fallback audio generation, using built-in Minecraft sound. This can be + // necessary on certain systems with audio cards that do not have enough + // memory. May still fail, but at least we can say we tried! + // Valid range is 20-2000Hz, clamp it to that and get a relative value. + // MC's pitch system supports a minimum pitch of 0.5, however, so up it + // by that. + val clampedFrequency = ((frequencyInHz - 20) max 0 min 1980) / 1980f + 0.5f + var delay = 0 + for (ch <- pattern) { + val record = new PositionedSoundRecord(new ResourceLocation("note.harp"), gain, clampedFrequency, x, y, z) + if (delay == 0) mc.getSoundHandler.playSound(record) + else mc.getSoundHandler.playDelayedSound(record, delay) + delay += ((if (ch == '.') durationInMilliseconds else 2 * durationInMilliseconds) * 20 / 1000) max 1 + } + } + else { + if (AL.isCreated) { val sampleCounts = pattern.toCharArray. map(ch => if (ch == '.') durationInMilliseconds else 2 * durationInMilliseconds). map(_ * sampleRate / 1000) diff --git a/src/main/scala/li/cil/oc/util/ItemUtils.scala b/src/main/scala/li/cil/oc/util/ItemUtils.scala index b84853b13..0da7adf79 100644 --- a/src/main/scala/li/cil/oc/util/ItemUtils.scala +++ b/src/main/scala/li/cil/oc/util/ItemUtils.scala @@ -73,13 +73,13 @@ object ItemUtils { def getIngredients(stack: ItemStack): Array[ItemStack] = try { def getFilteredInputs(inputs: Iterable[ItemStack], outputSize: Double) = inputs.filter(input => input != null && - input.getItem != null && - math.floor(input.stackSize / outputSize) > 0 && - // Strip out buckets, because those are returned when crafting, and - // we have no way of returning the fluid only (and I can't be arsed - // to make it output fluids into fluiducts or such, sorry). - !input.getItem.isInstanceOf[ItemBucket]).toArray - def getOutputSize(recipe: IRecipe) = + input.getItem != null && + math.floor(input.stackSize / outputSize) > 0 && + // Strip out buckets, because those are returned when crafting, and + // we have no way of returning the fluid only (and I can't be arsed + // to make it output fluids into fluiducts or such, sorry). + !input.getItem.isInstanceOf[ItemBucket]).toArray + def getOutputSize(recipe: IRecipe): Double = if (recipe != null && recipe.getRecipeOutput != null) recipe.getRecipeOutput.stackSize else @@ -93,11 +93,11 @@ object ItemUtils { case Some(recipe: ShapelessRecipes) => getFilteredInputs(recipe.recipeItems.map(_.asInstanceOf[ItemStack]), getOutputSize(recipe)) case Some(recipe: ShapedOreRecipe) => getFilteredInputs(resolveOreDictEntries(recipe.getInput), getOutputSize(recipe)) case Some(recipe: ShapelessOreRecipe) => getFilteredInputs(resolveOreDictEntries(recipe.getInput), getOutputSize(recipe)) - case _ => Array.empty + case _ => Array.empty[ItemStack] } // Avoid positive feedback loops. if (ingredients.exists(ingredient => ingredient.isItemEqual(stack))) { - return Array.empty + return Array.empty[ItemStack] } // Merge equal items for size division by output size. val merged = mutable.ArrayBuffer.empty[ItemStack] @@ -122,7 +122,7 @@ object ItemUtils { catch { case t: Throwable => OpenComputers.log.warn("Whoops, something went wrong when trying to figure out an item's parts.", t) - Array.empty + Array.empty[ItemStack] } private lazy val rng = new Random()