diff --git a/src/main/resources/LICENSE b/src/main/resources/LICENSE index c17a158fd..68dd6e055 100644 --- a/src/main/resources/LICENSE +++ b/src/main/resources/LICENSE @@ -16,4 +16,11 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file +THE SOFTWARE. + +------------------------------------------------------------------------------- + +All images / textures and localization strings (resources) are put in the +public domain. More specicially, see CC0 1.0 Universal: + + http://creativecommons.org/publicdomain/zero/1.0/ \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/lang/de_DE.lang b/src/main/resources/assets/opencomputers/lang/de_DE.lang index 9c4eba203..c9f1bc053 100644 --- a/src/main/resources/assets/opencomputers/lang/de_DE.lang +++ b/src/main/resources/assets/opencomputers/lang/de_DE.lang @@ -133,7 +133,7 @@ oc:tooltip.Disk=Sehr einfaches Speichermedium, das verwendet werden kann, um Dis oc:tooltip.DiskDrive.CC=ComputerCraft-Disketten werden §aunterstützt§7. oc:tooltip.DiskDrive=Erlaubt es, Disketten zu lesen und zu beschreiben. oc:tooltip.GraphicsCard=Erlaubt es, den angezeigten Inhalt von Bildschirmen zu ändern.[nl] Höchstauflösung: §f%sx%s§7.[nl] Maximale Farbtiefe: §f%s§7.[nl] Operationen/Tick: §f%s§7. -oc:tooltip.InternetCard=Diese Karte erlaubt es, HTTP-Anfragen zu senden und echte TCP und UDP Sockets zu verwenden. +oc:tooltip.InternetCard=Diese Karte erlaubt es, HTTP-Anfragen zu senden und echte TCP Sockets zu verwenden. oc:tooltip.IronNugget=Ein Nugget, das aus Eisen besteht, darum wird es ja auch Eisennugget genannt, duh... oc:tooltip.Keyboard=Kann an Bildschirmen befestigt werden, um auf ihnen zu tippen. oc:tooltip.Memory=Braucht ein jeder Computer, um zu starten. Je mehr vorhanden, desto komplexere Programme können ausgeführt werden. diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index 22abe2be9..b5f8195d6 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -133,7 +133,7 @@ oc:tooltip.Disk=Primitive medium that can be used to build persistent storage de oc:tooltip.DiskDrive.CC=ComputerCraft floppies are §asupported§7. oc:tooltip.DiskDrive=Allows reading and writing floppies. oc:tooltip.GraphicsCard=Used to change what's displayed on screens.[nl] Maximum resolution: §f%sx%s§7.[nl] Maximum color depth: §f%s§7.[nl] Operations/tick: §f%s§7. -oc:tooltip.InternetCard=This card allows making HTTP requests and using real TCP and UDP sockets. +oc:tooltip.InternetCard=This card allows making HTTP requests and using real TCP sockets. oc:tooltip.IronNugget=A nugget made of iron, that's why it's called an Iron Nugget, duh... oc:tooltip.Keyboard=Can be attached to screens to allow typing on them. oc:tooltip.Memory=Required to get computers to run. The more you have, the more complex the programs you can run. diff --git a/src/main/resources/assets/opencomputers/lang/ru_RU.lang b/src/main/resources/assets/opencomputers/lang/ru_RU.lang new file mode 100644 index 000000000..af9879af1 --- /dev/null +++ b/src/main/resources/assets/opencomputers/lang/ru_RU.lang @@ -0,0 +1,170 @@ +# Russian localization by YuRaNnNzZZ +# Use UTF-8 for this file. + +# Blocks +oc:block.Adapter.name=Адаптер +oc:block.Cable.name=Кабель +oc:block.Capacitor.name=Аккумулятор +oc:block.Case0.name=Системный блок 1-го уровня +oc:block.Case1.name=Системный блок 2-го уровня +oc:block.Case2.name=Системный блок 3-го уровня +oc:block.Charger.name=Зарядное устройство +oc:block.DiskDrive.name=Дисковод +oc:block.Keyboard.name=Клавиатура +oc:block.PowerConverter.name=Преобразователь энергии +oc:block.PowerDistributor.name=Передатчик энергии +oc:block.Redstone.name=Обработчик сигналов красной пыли +oc:block.Robot.name=Робот +oc:block.RobotAfterimage.name=Робот +oc:block.Router.name=Маршрутизатор +oc:block.Screen0.name=Дисплей (Ур. 1) +oc:block.Screen1.name=Дисплей (Ур. 2) +oc:block.Screen2.name=Дисплей (Ур. 3) +oc:block.ServerRack.name=Серверная стойка + +# Items +oc:item.AbstractBusCard.name=Плата абстрактной шины +oc:item.Acid.name=Кислота +oc:item.ALU.name=Арифметико-логическое устройство +oc:item.Analyzer.name=Анализатор +oc:item.ArrowKeys.name=Клавиши со стрелками +oc:item.ButtonGroup.name=Группа клавиш +oc:item.CardBase.name=Основа для карт +oc:item.CircuitBoard.name=Печатная плата +oc:item.ControlUnit.name=Блок управления +oc:item.CPU0.name=Центральный процессор (Ур. 1) +oc:item.CPU1.name=Центральный процессор (Ур. 2) +oc:item.CPU2.name=Центральный процессор (Ур. 3) +oc:item.CuttingWire.name=Проволока +oc:item.Disk.name=Металлический диск +oc:item.FloppyDisk.name=Дискета +oc:item.GraphicsCard0.name=Видеокарта (Ур. 1) +oc:item.GraphicsCard1.name=Видеокарта (Ур. 2) +oc:item.GraphicsCard2.name=Видеокарта (Ур. 3) +oc:item.HardDiskDrive0.name=Жёсткий диск (Ур. 1) +oc:item.HardDiskDrive1.name=Жёсткий диск (Ур. 2) +oc:item.HardDiskDrive2.name=Жёсткий диск (Ур. 3) +oc:item.InternetCard.name=Интернет-адаптер +oc:item.IronNugget.name=Железный самородок +oc:item.Memory0.name=ОЗУ (Ур. 1) +oc:item.Memory1.name=ОЗУ (Ур. 2) +oc:item.Memory2.name=ОЗУ (Ур. 2.5) +oc:item.Memory3.name=ОЗУ (Ур. 3) +oc:item.Memory4.name=ОЗУ (Ур. 3.5) +oc:item.Microchip0.name=Микросхема (Ур. 1) +oc:item.Microchip1.name=Микросхема (Ур. 2) +oc:item.Microchip2.name=Микросхема (Ур. 3) +oc:item.NetworkCard.name=Сетевой адаптер +oc:item.NumPad.name=Цифровые клавиши +oc:item.PrintedCircuitBoard.name=Печатная плата +oc:item.RawCircuitBoard.name=Основа для печатной платы +oc:item.RedstoneCard.name=Плата-обработчик сигналов красной пыли +oc:item.Server0.name=Сервер (Ур. 1) +oc:item.Server1.name=Сервер (Ур. 2) +oc:item.Server2.name=Сервер (Ур. 3) +oc:item.Terminal.name=Беспроводной терминал +oc:item.Transistor.name=Транзистор +oc:item.UpgradeCrafting.name=Верстак (Улучшение) +oc:item.UpgradeGenerator.name=Топливный генератор (Улучшение) +oc:item.UpgradeNavigation.name=Навигатор (Улучшение) +oc:item.UpgradeSign.name=Работа с табличками (Улучшение) +oc:item.UpgradeSolarGenerator.name=Солнечная панель (Улучшение) +oc:item.WirelessNetworkCard.name=Адаптер беспроводной сети + +# GUI +oc:gui.Analyzer.Address=§6Адрес§f: %s +oc:gui.Analyzer.AddressCopied=Адрес скопирован в буфер обмена. +oc:gui.Analyzer.ChargerSpeed=§6Скорость зарядки§f: %s +oc:gui.Analyzer.ComponentName=§6Компонент§f: %s +oc:gui.Analyzer.Components=§6Кол-во подключенных компонентов§f: %s +oc:gui.Analyzer.LastError=§6Последняя ошибка§f: %s +oc:gui.Analyzer.RobotName=§6Имя робота§f: %s +oc:gui.Analyzer.RobotOwner=§6Владелец робота§f: %s +oc:gui.Analyzer.RobotXp=§6Опыт робрта§f: %s (%s уровень) +oc:gui.Analyzer.StoredEnergy=§6Накоплено энергии§f: %s +oc:gui.Analyzer.TotalEnergy=§6Всего накоплено энергии§f: %s +oc:gui.Analyzer.Users=§6Пользователи§f: %s +oc:gui.Chat.WarningLuaFallback=Родные библиотеки Lua не доступны, компьютеры не смогут сохранять своё состояние. Они перезагрузятся после выгрузки чанка. +oc:gui.Chat.WarningPower=Universal Electricity 3 недоступен. Компьютеры, дисплеи и другие компоненты §lне§f используют энергю. Заметьте, что UE3 используется для поддержки BuildCraft, IndustrialCraft2 и Thermal Expansion. +oc:gui.Chat.WarningProjectRed=Вы используете версию Project: Red, несовместимую с OpenComputers. Попробуйте обновить Project: Red. +oc:gui.Error.ComponentOverflow=Слишком много компонентов подключено к компьютеру. +oc:gui.Error.DaylightCycle=Пожалуйста, включите doDaylightCycle для работы компьютеров. +oc:gui.Error.InternalError=Внутренняя ошибка, пожалуйста посмотрите лог-файл. Возможно это баг. +oc:gui.Error.NoCPU=Не установлен центральный процессор. +oc:gui.Error.NoEnergy=Недостаточно энергии. +oc:gui.Error.NoRAM=Не установлена ОЗУ. +oc:gui.Error.OutOfMemory=Недостаточно памяти. +oc:gui.Robot.Power=Энергия +oc:gui.Robot.TurnOff=Выключить робота +oc:gui.Robot.TurnOn=Включить робота +oc:gui.ServerRack.None=Нет +oc:gui.ServerRack.Back=Сзади +oc:gui.ServerRack.Bottom=Снизу +oc:gui.ServerRack.Left=Слева +oc:gui.ServerRack.Right=Справа +oc:gui.ServerRack.Top=Сверху +oc:gui.ServerRack.WirelessRange=Радиус беспроводной сети +oc:gui.Terminal.InvalidKey=Неверный ключ, возможно к серверу уже подключен другой терминал. +oc:gui.Terminal.OutOfRange=Нет сигнала от сервера. + +# Containers +oc:container.Case=Системный блок +oc:container.DiskDrive=Дисковод +oc:container.Rack=Серверная стойка +oc:container.Server=Сервер + +# Item / Block Tooltips +oc:tooltip.AbstractBusCard=Позволяет использовать отправлять и принимать LIP-пакеты через абстрактную шину из мода §fStargateTech 2§7. +oc:tooltip.Acid=Высокотоксичная псевдо-жидкость, как правило, употребляемая некоторыми пиратами. Благодаря своей агрессивной природе она идеально подходит для травления печатных плат. +oc:tooltip.Adapter=Используется для контроля некомпонентных блоков, например блоков из других модов. +oc:tooltip.ALU=Выполняет арифметические вычисления, так что вам не придётся возиться с ними. +oc:tooltip.Analyzer=Используется для отображения информации о компонентах, например их §fадрес§7 или §fимя§7.[nl] Также отображает ошибку, вызвавшую сбой компонента. +oc:tooltip.Cable=Лёгкий и дешёвый способ соединить компоненты между собой. +oc:tooltip.Capacitor=Накапливает энергию для дальнейшего использования. Может быть очень быстро заполнен и опустошён. +oc:tooltip.CardBase=Можно понять по названию, что это основа для всех карт расширения. +oc:tooltip.Case=Системный блок - основа компьютера и может быть улучшен §fкартами расширения§7, §fОЗУ§7 и §fжёсткими дисками§7.[nl] Слоты: §f%s§7. +oc:tooltip.Charger=Передаёт энергию из аккумуляторов роботам. Скорость зарядки зависит от §fсилы сигнала красной пыли§7, где отсутствие сигнала означает не заряжать, а полный сигнал - заряжать на полной скорости. +oc:tooltip.CircuitBoard=Мы уже очень близки. Может быть вытравлена, чтобы получить печатную плату. +oc:tooltip.ControlUnit=Это штука, которая... контролирует... что-то. Она необходима, чтобы создать центральный процессор. Да, она очень важна. +oc:tooltip.CPU=Является важным компонентом в компьютере. Тактовая частота немного нестабильна, но что вы ожидали, когда он работает даже на карманных солнечных часах? +oc:tooltip.CuttingWire=Используется для нарезки глиняных блоков в пластины. Рвётся после использования, что делает её очень неэффективным инструментом. +oc:tooltip.Disk=Примитивный носитель, который может быть использован для создания постоянных запоминающих устройств. +oc:tooltip.DiskDrive.CC=§aПоддерживаются§7 дискеты из ComputerCraft. +oc:tooltip.DiskDrive=Позволяет читать и записывать дискеты. +oc:tooltip.GraphicsCard=Передаёт изображение на дисплей.[nl] Максимальное разрешение: §f%sx%s§7.[nl] Максимальная г лубина цвета:§f%s§7.[nl] Операций/тик: §f%s§7. +oc:tooltip.InternetCard=Эта карта позволяет создавать HTTP-запросы и использует реальные TCP сокеты. +oc:tooltip.IronNugget=Самородок, созданный из железа. Может именно поэтому он и назван железным самородком, нет? +oc:tooltip.Keyboard=Может быть прикреплена к дисплеям, позволяя вводить информацию. +oc:tooltip.Memory=Необходима для запуска компьютера. Чем больше ОЗУ, тем более требовательные программы вы можете запустить. +oc:tooltip.Microchip=Схема, ранее известная как интегральная. Понятия не имею, как она работает с красной пылью, но она работает. +oc:tooltip.NetworkCard=Позволяет соединить компьютеры, удалённые друг от друга, в сеть для отправки сообщений между ними. +oc:tooltip.PowerConverter.BC=§fBuildCraft MJ§7: §a%s:%s§7. +oc:tooltip.PowerConverter.IC2=§fIndustrialCraft² EU§7: §a%s:%s§7. +oc:tooltip.PowerConverter.TE=§fThermal Expansion RF§7: §a%s:%s§7. +oc:tooltip.PowerConverter.UE=§fUniversal Electricity Joules§7: §a%s:%s§7. +oc:tooltip.PowerConverter=Конвертирует энергию из других модов для питания компонентов.[nl] Соотношения энергии: +oc:tooltip.PowerDistributor=Передаёт энергию между различными сетями. Это полезно, чтобы запитать несколько сетей, которые должны оставаться раздельными друг от друга. +oc:tooltip.PrintedCircuitBoard=Основа для плат, ОЗУ и прочего. +oc:tooltip.RawCircuitBoard=Может быть закалена в печи для получения печатной платы. +oc:tooltip.Redstone=Позволяет принимать и излучать сигналы красной пыли вокруг компонента. Контролируется любым подключенным компьютером. +oc:tooltip.RedstoneCard.RedLogic=§aПоддерживаются§7 провода из §fRedLogic§7. +oc:tooltip.RedstoneCard.RedNet=§aПоддерживается§7 провода из §fRedNet§7. +oc:tooltip.RedstoneCard=Позволяет принимать и излучать сигналы красной пыли вокруг компьютера или робота. +oc:tooltip.Robot=В отличие от компьютеров, роботы могут передвигаться и взаимодействовать с миром как игрок. Но они не могут §oвзаимодействовать§r§7 с остальными компонентами. +# The underscore makes sure this isn't hidden with the rest of the tooltip. +oc:tooltip.Robot_Level=§fОпыт робота§7: §a%s§7. +oc:tooltip.Robot_StoredEnergy=§fНакопленная энергия§7: §a%s§7. +oc:tooltip.Router=Позволяет соединять различные сети между собой. Передаются только сообщения, компоненты между сетями недоступны. Используйте его для разделения сетей с возможностью пересылать сообщения между ними. +oc:tooltip.Screen=Отображает текст, передаваемый видеокартой.[nl] Максимальное разрешение экрана: §f%sx%s§7.[nl] Максимальная глубина цвета: §f%s§7. +oc:tooltip.Server=Это сервер, который может быть улучшен компонентами, как системный блок. Может быть включен, будучи вставленным в стойку.[nl] Количество поддерживаемых терминалов: §f%s§7. +oc:tooltip.Server.Components=Установленные компоненты: +oc:tooltip.ServerRack=Обеспечивает работу до четырёх серверов. Предоставляет виртуальную клавиатуру и дисплей для подключения беспроводного терминала. +oc:tooltip.Terminal=Позволяет дистанционно управлять сервером, пока вы в радиусе его действия. Действует как портативный дисплей с клавиатурой.[nl] Shift+ПКМ на сервер в стойке для привязки терминала. +oc:tooltip.TooLong=Удерживайте Shift для расширенного описания. +oc:tooltip.Transistor=Базовый элемент в других частях компьютера. Он немного деформирован, но отлично выполняет свою работу. +oc:tooltip.UpgradeCrafting=Позволяет роботам использовать верхнюю левую часть инвентаря для создания вещей. Вещи должны быть расположены в том порядке, что и в верстаке. +oc:tooltip.UpgradeGenerator=Позволяет роботам генерировать энергию из сжигаемого топлива прямо на ходу.[nl] §fЭффективность§7: §a%s%%§7 +oc:tooltip.UpgradeNavigation=Позволяет определять положение и ориентацию робота. Положение определяется относительно центра карты. +oc:tooltip.UpgradeSign=Позволяет читать и писать текст на табличках. +oc:tooltip.UpgradeSolarGenerator=Позволяет роботам генерировать энергию из солнечного света прямо на ходу. Требует линию прямой видимости неба над роботом. Генерирует энергию на скорости %s%% от топливного генератора. +oc:tooltip.WirelessNetworkCard=Позволяет передавать сетевые сообщения по беспроводному каналу. Не забудьте выставить §fсилу сигнала красной пыли§7, или сообщения отправлены не будут! diff --git a/src/main/resources/assets/opencomputers/lua/kernel.lua b/src/main/resources/assets/opencomputers/lua/kernel.lua index aae4f1804..cf313eb7a 100644 --- a/src/main/resources/assets/opencomputers/lua/kernel.lua +++ b/src/main/resources/assets/opencomputers/lua/kernel.lua @@ -259,7 +259,17 @@ sandbox._G = sandbox ------------------------------------------------------------------------------- -- Start of non-standard stuff made available via package.preload. -local libcomponent = { +local libcomponent +libcomponent = { + doc = function(address, method) + checkArg(1, address, "string") + checkArg(2, method, "string") + local result, reason = component.doc(address, method) + if not result and reason then + error(reason, 2) + end + return result + end, invoke = function(address, method, ...) checkArg(1, address, "string") checkArg(2, method, "string") @@ -288,9 +298,14 @@ local libcomponent = { return nil, reason end for method, direct in pairs(methods) do - proxy[method] = function(...) - return invoke(direct, address, method, ...) - end + proxy[method] = setmetatable({}, { + __call = function(_, ...) + return invoke(direct, address, method, ...) + end, + __tostring = function() + return libcomponent.doc(address, method) or "function" + end + }) end return proxy end, diff --git a/src/main/resources/assets/opencomputers/lua/rom/lib/text.lua b/src/main/resources/assets/opencomputers/lua/rom/lib/text.lua index 98ef4d588..ebca7a9a4 100644 --- a/src/main/resources/assets/opencomputers/lua/rom/lib/text.lua +++ b/src/main/resources/assets/opencomputers/lua/rom/lib/text.lua @@ -95,7 +95,7 @@ function text.serialize(value, pretty) ["until"]=true, ["while"]=true} local id = "^[%a_][%w_]*$" local ts = {} - local function s(v) + local function s(v, l) local t = type(v) if t == "nil" then return "nil" @@ -113,6 +113,8 @@ function text.serialize(value, pretty) end elseif t == "string" then return string.format("%q", v) + elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then + return tostring(v) elseif t == "table" then if ts[v] then if pretty then @@ -122,24 +124,36 @@ function text.serialize(value, pretty) end end ts[v] = true - local i, r = 1, nil - for k, v in pairs(v) do + local f, i, r = 1, nil + if pretty then + local ks = {} + for k in pairs(v) do table.insert(ks, k) end + table.sort(ks) + local k = 0 + f = function() + k = k + 1 + return ks[k], ks[k] and v[ks[k]] or nil + end + else + f = pairs(v) + end + for k, v in f do if r then - r = r .. "," + r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "") else r = "{" end local tk = type(k) if tk == "number" and k == i then i = i + 1 - r = r .. s(v) + r = r .. s(v, l + 1) else if tk == "string" and not kw[k] and string.match(k, id) then r = r .. k else - r = r .. "[" .. s(k) .. "]" + r = r .. "[" .. s(k, l + 1) .. "]" end - r = r .. "=" .. s(v) + r = r .. "=" .. s(v, l + 1) end end ts[v] = nil -- allow writing same table more than once @@ -152,7 +166,7 @@ function text.serialize(value, pretty) end end end - local result = s(value) + local result = s(value, 1) local limit = type(pretty) == "number" and pretty or 1000 if pretty and unicode.len(result) > limit then return result:sub(1, limit) .. "..." diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 6a39cc880..ec3178bd4 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -86,7 +86,7 @@ opencomputers { activeGC: true # The sizes of the three tiers of RAM, in kilobytes. This list must - # contain exactly three entries, or it will be ignored. + # contain exactly five entries, or it will be ignored. ramSizes: [ 64 128 diff --git a/src/main/scala/li/cil/oc/common/container/Case.scala b/src/main/scala/li/cil/oc/common/container/Case.scala index 08b9fbdd3..1895fc247 100644 --- a/src/main/scala/li/cil/oc/common/container/Case.scala +++ b/src/main/scala/li/cil/oc/common/container/Case.scala @@ -23,6 +23,10 @@ class Case(playerInventory: InventoryPlayer, computer: tileentity.Case) extends addSlotToContainer(120, 16, api.driver.Slot.Processor, computer.tier) + if (computer.tier == 0) { + addSlotToContainer(120, 16 + 2 * slotSize, api.driver.Slot.Memory, computer.maxComponentTierForSlot(getInventory.size)) + } + // Show the player's inventory. addPlayerInventorySlots(8, 84) diff --git a/src/main/scala/li/cil/oc/common/item/Alu.scala b/src/main/scala/li/cil/oc/common/item/ALU.scala similarity index 100% rename from src/main/scala/li/cil/oc/common/item/Alu.scala rename to src/main/scala/li/cil/oc/common/item/ALU.scala diff --git a/src/main/scala/li/cil/oc/common/tileentity/Case.scala b/src/main/scala/li/cil/oc/common/tileentity/Case.scala index e978e2de4..cc9ed1f5c 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Case.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Case.scala @@ -62,7 +62,7 @@ class Case(var tier: Int, isRemote: Boolean) extends Computer(isRemote) { override def getInventoryName = Settings.namespace + "container.Case" override def getSizeInventory = tier match { - case 0 => 5 + case 0 => 6 case 1 => 7 case 2 => 9 case _ => 0 @@ -79,7 +79,7 @@ class Case(var tier: Int, isRemote: Boolean) extends Computer(isRemote) { case 0 => (slot, Registry.itemDriverFor(stack)) match { case (_, None) => false // Invalid item. case (0 | 1, Some(driver)) => driver.slot(stack) == Slot.Card && driver.tier(stack) <= maxComponentTierForSlot(slot) - case (2, Some(driver)) => driver.slot(stack) == Slot.Memory && driver.tier(stack) <= maxComponentTierForSlot(slot) + case (2 | 5, Some(driver)) => driver.slot(stack) == Slot.Memory && driver.tier(stack) <= maxComponentTierForSlot(slot) case (3, Some(driver)) => driver.slot(stack) == Slot.HardDiskDrive && driver.tier(stack) <= maxComponentTierForSlot(slot) case (4, Some(driver)) => driver.slot(stack) == Slot.Processor && driver.tier(stack) <= maxComponentTierForSlot(slot) case _ => false // Invalid slot. @@ -105,7 +105,7 @@ class Case(var tier: Int, isRemote: Boolean) extends Computer(isRemote) { } def maxComponentTierForSlot(slot: Int) = tier match { - case 0 => 0 + case 0 if slot >= 0 && slot < getSizeInventory => 0 case 1 => slot match { case 0 => 1 case 1 => 0 diff --git a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala index 72a4dfad8..1bedcb1f1 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/RobotProxy.scala @@ -46,15 +46,15 @@ class RobotProxy(val robot: Robot) extends Computer(robot.isClient) with ISidedI // ----------------------------------------------------------------------- // - @Callback + @Callback(doc = """function():boolean -- Starts the robot. Returns true if the state changed.""") def start(context: Context, args: Arguments): Array[AnyRef] = result(!computer.isPaused && computer.start()) - @Callback + @Callback(doc = """function():boolean -- Stops the robot. Returns true if the state changed.""") def stop(context: Context, args: Arguments): Array[AnyRef] = result(computer.stop()) - @Callback(direct = true) + @Callback(direct = true, doc = """function():boolean -- Returns whether the robot is running.""") def isRunning(context: Context, args: Arguments): Array[AnyRef] = result(computer.isRunning) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala index 355bce0a0..c841f1ce3 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Screen.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Screen.scala @@ -25,10 +25,10 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable // ----------------------------------------------------------------------- // override protected val _buffer = new component.Buffer(this) { - @Callback + @Callback(doc = """function():boolean -- Returns whether the screen is currently on.""") def isOn(computer: Context, args: Arguments): Array[AnyRef] = result(origin.isOn) - @Callback + @Callback(doc = """function():boolean -- Turns off the screen. Returns true if it was on.""") def turnOn(computer: Context, args: Arguments): Array[AnyRef] = { if (!origin.isOn) { origin.turnOn() @@ -37,7 +37,7 @@ class Screen(var tier: Int) extends Buffer with SidedEnvironment with Rotatable else result(false, origin.isOn) } - @Callback + @Callback(doc = """function():boolean -- Turns the screen on. Returns true if it was off.""") def turnOff(computer: Context, args: Arguments): Array[AnyRef] = { if (origin.isOn) { origin.turnOff() diff --git a/src/main/scala/li/cil/oc/server/component/AbstractBus.scala b/src/main/scala/li/cil/oc/server/component/AbstractBus.scala index 359ba72e6..62b4c4bf2 100644 --- a/src/main/scala/li/cil/oc/server/component/AbstractBus.scala +++ b/src/main/scala/li/cil/oc/server/component/AbstractBus.scala @@ -54,25 +54,25 @@ class AbstractBus(val device: IBusDevice) extends ManagedComponent with IBusDriv // ----------------------------------------------------------------------- // - @Callback + @Callback(doc = """function():boolean -- Whether the local bus interface is enabled.""") def getEnabled(context: Context, args: Arguments): Array[AnyRef] = result(isEnabled) - @Callback + @Callback(doc = """function(enabled:boolean):boolean -- Sets whether the local bus interface should be enabled.""") def setEnabled(context: Context, args: Arguments): Array[AnyRef] = { isEnabled = args.checkBoolean(0) result(isEnabled) } - @Callback + @Callback(doc = """function():number -- Get the local interface address.""") def getAddress(context: Context, args: Arguments): Array[AnyRef] = result(address) - @Callback + @Callback(doc = """function(address:number):number -- Sets the local interface address.""") def setAddress(context: Context, args: Arguments): Array[AnyRef] = { address = args.checkInteger(0) & 0xFFFF result(address) } - @Callback + @Callback(doc = """function(address:number, data:table):boolean -- Sends data across the abstract bus.""") def send(context: Context, args: Arguments): Array[AnyRef] = { val target = args.checkInteger(0) & 0xFFFF val data = args.checkTable(1) @@ -84,7 +84,7 @@ class AbstractBus(val device: IBusDevice) extends ManagedComponent with IBusDriv else result(false, "not enough energy") } - @Callback(direct = true) + @Callback(direct = true, doc = """function():number -- The maximum packet size that can be sent over the bus.""") def maxPacketSize(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.maxNetworkPacketSize) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/component/BundledRedstone.scala b/src/main/scala/li/cil/oc/server/component/BundledRedstone.scala index 61ea3e42c..d8325a4cb 100644 --- a/src/main/scala/li/cil/oc/server/component/BundledRedstone.scala +++ b/src/main/scala/li/cil/oc/server/component/BundledRedstone.scala @@ -5,21 +5,21 @@ import li.cil.oc.common.tileentity.BundledRedstoneAware class BundledRedstone(override val owner: BundledRedstoneAware) extends Redstone(owner) { - @Callback(direct = true) + @Callback(direct = true, doc = """function(side:number, color:number):number -- Get the bundled redstone input on the specified side and with the specified color.""") def getBundledInput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) val color = checkColor(args, 1) result(owner.bundledInput(side, color)) } - @Callback(direct = true) + @Callback(direct = true, doc = """function(side:number, color:number):number -- Get the bundled redstone output on the specified side and with the specified color.""") def getBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) val color = checkColor(args, 1) result(owner.bundledOutput(side, color)) } - @Callback + @Callback(doc = """function(side:number, color:number, value:number):number -- Set the bundled redstone output on the specified side and with the specified color.""") def setBundledOutput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) val color = checkColor(args, 1) diff --git a/src/main/scala/li/cil/oc/server/component/Filesystem.scala b/src/main/scala/li/cil/oc/server/component/FileSystem.scala similarity index 100% rename from src/main/scala/li/cil/oc/server/component/Filesystem.scala rename to src/main/scala/li/cil/oc/server/component/FileSystem.scala diff --git a/src/main/scala/li/cil/oc/server/component/InternetCard.scala b/src/main/scala/li/cil/oc/server/component/InternetCard.scala index 8162c1c1c..538ae6bca 100644 --- a/src/main/scala/li/cil/oc/server/component/InternetCard.scala +++ b/src/main/scala/li/cil/oc/server/component/InternetCard.scala @@ -44,10 +44,10 @@ class InternetCard extends ManagedComponent { // ----------------------------------------------------------------------- // - @Callback(direct = true) + @Callback(direct = true, doc = """function():boolean -- Returns whether HTTP requests can be made (config setting).""") def isHttpEnabled(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.httpEnabled) - @Callback + @Callback(doc = """function():boolean -- Starts an HTTP request. If this returns true, further results will be pushed using `http_response` signals.""") def request(context: Context, args: Arguments): Array[AnyRef] = { if (owner.isEmpty || context.node.address != owner.get.node.address) { throw new IllegalArgumentException("can only be used by the owning computer") @@ -130,10 +130,10 @@ class InternetCard extends ManagedComponent { }) } - @Callback(direct = true) + @Callback(direct = true, doc = """function():boolean -- Returns whether TCP connections can be made (config setting).""") def isTcpEnabled(context: Context, args: Arguments): Array[AnyRef] = result(Settings.get.httpEnabled) - @Callback + @Callback(doc = """function(address:string[, port:number]):number -- Opens a new TCP connection. Returns the handle of the connection.""") def connect(context: Context, args: Arguments): Array[AnyRef] = { val address = args.checkString(0) val port = if (args.count > 1) args.checkInteger(1) else -1 @@ -151,7 +151,7 @@ class InternetCard extends ManagedComponent { result(handle) } - @Callback + @Callback(doc = """function(handle:number) -- Closes an open socket stream.""") def close(context: Context, args: Arguments): Array[AnyRef] = { val handle = args.checkInteger(0) connections.remove(handle) match { @@ -161,7 +161,7 @@ class InternetCard extends ManagedComponent { null } - @Callback + @Callback(doc = """function(handle:number, data:string):number -- Tries to write data to the socket stream. Returns the number of bytes written.""") def write(context: Context, args: Arguments): Array[AnyRef] = { val handle = args.checkInteger(0) val value = args.checkByteArray(1) @@ -173,7 +173,7 @@ class InternetCard extends ManagedComponent { } } - @Callback + @Callback(doc = """function(handle:number, n:number):string -- Tries to read data from the socket stream. Returns the read byte array.""") def read(context: Context, args: Arguments): Array[AnyRef] = { val handle = args.checkInteger(0) val n = math.min(Settings.get.maxReadBuffer, math.max(0, args.checkInteger(1))) 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 54f0aec30..862b4da05 100644 --- a/src/main/scala/li/cil/oc/server/component/NetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/NetworkCard.scala @@ -16,17 +16,18 @@ class NetworkCard extends ManagedComponent { // ----------------------------------------------------------------------- // - @Callback + @Callback(doc = """function(port:number):boolean -- Opens the specified port. Returns true if the port was opened.""") def open(context: Context, args: Arguments): Array[AnyRef] = { val port = checkPort(args.checkInteger(0)) result(openPorts.add(port)) } - @Callback + @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 openPorts.clear() - result(true) + result(closed) } else { val port = checkPort(args.checkInteger(0)) @@ -34,16 +35,16 @@ class NetworkCard extends ManagedComponent { } } - @Callback(direct = true) + @Callback(direct = true, doc = """function(port:number):boolean -- Whether the specified port is open.""") def isOpen(context: Context, args: Arguments): Array[AnyRef] = { val port = checkPort(args.checkInteger(0)) result(openPorts.contains(port)) } - @Callback(direct = true) + @Callback(direct = true, doc = """function():boolean -- Whether this is a wireless network card.""") def isWireless(context: Context, args: Arguments): Array[AnyRef] = result(false) - @Callback + @Callback(doc = """function(address:string, port:number, data...) -- Sends the specified data to the specified target.""") def send(context: Context, args: Arguments): Array[AnyRef] = { val address = args.checkString(0) val port = checkPort(args.checkInteger(1)) @@ -52,7 +53,7 @@ class NetworkCard extends ManagedComponent { result(true) } - @Callback + @Callback(doc = """function(port:number, data...) -- Broadcasts the specified data on the specified port.""") def broadcast(context: Context, args: Arguments): Array[AnyRef] = { val port = checkPort(args.checkInteger(0)) checkPacketSize(args.drop(1)) @@ -60,7 +61,7 @@ class NetworkCard extends ManagedComponent { result(true) } - @Callback(direct = true) + @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) // ----------------------------------------------------------------------- // diff --git a/src/main/scala/li/cil/oc/server/component/Redstone.scala b/src/main/scala/li/cil/oc/server/component/Redstone.scala index 53196dc96..36a24e1d4 100644 --- a/src/main/scala/li/cil/oc/server/component/Redstone.scala +++ b/src/main/scala/li/cil/oc/server/component/Redstone.scala @@ -12,19 +12,19 @@ class Redstone(val owner: RedstoneAware) extends ManagedComponent { // ----------------------------------------------------------------------- // - @Callback(direct = true) + @Callback(direct = true, doc = """function(side:number):number -- Get the redstone input on the specified side.""") def getInput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) result(owner.input(side)) } - @Callback(direct = true) + @Callback(direct = true, doc = """function(side:number):number -- Get the redstone output on the specified side.""") def getOutput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) result(owner.output(side)) } - @Callback() + @Callback(doc = """function(side:number, value:number):number -- Set the redstone output on the specified side.""") def setOutput(context: Context, args: Arguments): Array[AnyRef] = { val side = checkSide(args, 0) val value = args.checkInteger(1) diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala index a5ec73e70..568174212 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeCrafting.scala @@ -17,7 +17,7 @@ class UpgradeCrafting(val owner: MCTileEntity) extends ManagedComponent { withComponent("crafting"). create() - @Callback + @Callback(doc = """function([count:number]):number -- Tries to craft the specified number of items in the top left area of the inventory.""") def craft(context: RobotContext, args: Arguments): Array[AnyRef] = { val count = if (args.count > 0) args.checkInteger(0) else Int.MaxValue result(CraftingInventory.craft(context, count)) diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala index 4f23f1a7a..91f8b7b14 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeGenerator.scala @@ -21,7 +21,7 @@ class UpgradeGenerator(val owner: TileEntity) extends ManagedComponent { // ----------------------------------------------------------------------- // - @Callback + @Callback(doc = """function([count:number]):boolean -- Tries to insert fuel from the selected slot into the generator's queue.""") def insert(context: RobotContext, args: Arguments): Array[AnyRef] = { val count = if (args.count > 0) args.checkInteger(0) else 64 val player = context.player @@ -48,7 +48,7 @@ class UpgradeGenerator(val owner: TileEntity) extends ManagedComponent { result(true) } - @Callback + @Callback(doc = """function():number -- Get the size of the item stack in the generator's queue.""") def count(context: Context, args: Arguments): Array[AnyRef] = { inventory match { case Some(stack) => result(stack.stackSize) @@ -56,7 +56,7 @@ class UpgradeGenerator(val owner: TileEntity) extends ManagedComponent { } } - @Callback + @Callback(doc = """function([count:number]):boolean -- Tries to remove items from the generator's queue.""") def remove(context: RobotContext, args: Arguments): Array[AnyRef] = { val count = if (args.count > 0) args.checkInteger(0) else Int.MaxValue inventory match { diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala index 1217fc632..1ad8fb8c9 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeNavigation.scala @@ -11,7 +11,7 @@ class UpgradeNavigation(val owner: TileEntity, val xCenter: Int, val zCenter: In // ----------------------------------------------------------------------- // - @Callback + @Callback(doc = """function():number, number, number -- Get the current relative position of the robot.""") def getPosition(context: Context, args: Arguments): Array[AnyRef] = { val x = owner.xCoord val y = owner.yCoord @@ -25,7 +25,7 @@ class UpgradeNavigation(val owner: TileEntity, val xCenter: Int, val zCenter: In result(Unit, "out of range") } - @Callback + @Callback(doc = """function():number -- Get the current orientation of the robot.""") def getFacing(context: Context, args: Arguments): Array[AnyRef] = { owner match { case rotatable: Rotatable => result(rotatable.facing.ordinal) @@ -33,7 +33,7 @@ class UpgradeNavigation(val owner: TileEntity, val xCenter: Int, val zCenter: In } } - @Callback + @Callback(doc = """function():number -- Get the operational range of the navigation upgrade.""") def getRange(context: Context, args: Arguments): Array[AnyRef] = { result(size / 2) } diff --git a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala index eb4420bc9..4f2481f79 100644 --- a/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala +++ b/src/main/scala/li/cil/oc/server/component/UpgradeSign.scala @@ -12,7 +12,7 @@ class UpgradeSign(val owner: TileEntity) extends ManagedComponent { // ----------------------------------------------------------------------- // - @Callback + @Callback(doc = """function():string -- Get the text on the sign in front of the robot.""") def getValue(context: Context, args: Arguments): Array[AnyRef] = { val facing = owner match { case rotatable: Rotatable => rotatable.facing @@ -24,7 +24,7 @@ class UpgradeSign(val owner: TileEntity) extends ManagedComponent { } } - @Callback + @Callback(doc = """function(value:string):string -- Set the text on the sign in front of the robot.""") def setValue(context: Context, args: Arguments): Array[AnyRef] = { val text = args.checkString(0).lines.padTo(4, "").map(line => if (line.length > 15) line.substring(0, 15) else line) val facing = owner match { diff --git a/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala b/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala index 23dd7ce07..e1a674f89 100644 --- a/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala +++ b/src/main/scala/li/cil/oc/server/component/WirelessNetworkCard.scala @@ -20,10 +20,10 @@ class WirelessNetworkCard(val owner: TileEntity) extends NetworkCard { // ----------------------------------------------------------------------- // - @Callback(direct = true) + @Callback(direct = true, doc = """function():number -- Get the signal strength (range) used when sending messages.""") def getStrength(context: Context, args: Arguments): Array[AnyRef] = result(strength) - @Callback + @Callback(doc = """function(strength:number):number -- Set the signal strength (range) used when sending messages.""") def setStrength(context: Context, args: Arguments): Array[AnyRef] = { strength = math.max(args.checkDouble(0), math.min(0, Settings.get.maxWirelessRange)) result(strength) diff --git a/src/main/scala/li/cil/oc/server/component/machine/Machine.scala b/src/main/scala/li/cil/oc/server/component/machine/Machine.scala index 1b990b3b4..d0a54793d 100644 --- a/src/main/scala/li/cil/oc/server/component/machine/Machine.scala +++ b/src/main/scala/li/cil/oc/server/component/machine/Machine.scala @@ -220,9 +220,14 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi counts(method) += 1 } component.invoke(method, this, args: _*) - case _ => throw new Exception("no such component") + case _ => throw new IllegalArgumentException("no such component") } + private[component] def doc(address: String, method: String) = Option(node.network.node(address)) match { + case Some(component: server.network.Component) if component.canBeSeenFrom(node) || component == node => component.doc(method) + case _ => throw new IllegalArgumentException("no such component") + } + private[component] def addUser(name: String) { if (_users.size >= Settings.get.maxUsers) throw new Exception("too many users") @@ -250,15 +255,15 @@ class Machine(val owner: Machine.Owner) extends ManagedComponent with Context wi // ----------------------------------------------------------------------- // - @Callback + @Callback(doc = """function():boolean -- Starts the computer. Returns true if the state changed.""") def start(context: Context, args: Arguments): Array[AnyRef] = result(!isPaused && start()) - @Callback + @Callback(doc = """function():boolean -- Stops the computer. Returns true if the state changed.""") def stop(context: Context, args: Arguments): Array[AnyRef] = result(stop()) - @Callback(direct = true) + @Callback(direct = true, doc = """function():boolean -- Returns whether the computer is running.""") def isRunning(context: Context, args: Arguments): Array[AnyRef] = result(isRunning) diff --git a/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala b/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala index b37f1aed3..ce360f5ca 100644 --- a/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala +++ b/src/main/scala/li/cil/oc/server/component/machine/NativeLuaArchitecture.scala @@ -1,5 +1,6 @@ package li.cil.oc.server.component.machine +import com.google.common.base.Strings import com.naef.jnlua._ import java.io.{IOException, FileNotFoundException} import java.util.logging.Level @@ -528,6 +529,29 @@ class NativeLuaArchitecture(val machine: Machine) extends Architecture { }) lua.setField(-2, "invoke") + lua.pushScalaFunction(lua => { + val address = lua.checkString(1) + val method = lua.checkString(2) + try { + val doc = machine.doc(address, method) + if (Strings.isNullOrEmpty(doc)) + lua.pushNil() + else + lua.pushString(doc) + 1 + } catch { + case e: NoSuchMethodException => + lua.pushNil() + lua.pushString("no such method") + 2 + case t: Throwable => + lua.pushNil() + lua.pushString(if (t.getMessage != null) t.getMessage else t.toString) + 2 + } + }) + lua.setField(-2, "doc") + lua.setGlobal("component") initPerms() diff --git a/src/main/scala/li/cil/oc/server/fs/Buffered.scala b/src/main/scala/li/cil/oc/server/fs/Buffered.scala index c1de16a15..c8a8077b1 100644 --- a/src/main/scala/li/cil/oc/server/fs/Buffered.scala +++ b/src/main/scala/li/cil/oc/server/fs/Buffered.scala @@ -55,7 +55,10 @@ trait Buffered extends OutputStreamFileSystem { } setLastModified(path, directory.lastModified()) } - recurse("", fileRoot) + if (fileRoot.list() == null || fileRoot.list().length == 0) { + fileRoot.delete() + } + else recurse("", fileRoot) super.load(nbt) } @@ -94,6 +97,9 @@ trait Buffered extends OutputStreamFileSystem { } directory.setLastModified(lastModified(path)) } - recurse("") + if (list("") == null || list("").length == 0) { + fileRoot.delete() + } + else recurse("") } } diff --git a/src/main/scala/li/cil/oc/server/network/Component.scala b/src/main/scala/li/cil/oc/server/network/Component.scala index c5f2a309c..3939889aa 100644 --- a/src/main/scala/li/cil/oc/server/network/Component.scala +++ b/src/main/scala/li/cil/oc/server/network/Component.scala @@ -99,6 +99,11 @@ trait Component extends network.Component with Node { def methods = callbacks.keySet + def doc(name: String) = callbacks.get(name) match { + case Some(callback) => callback.doc + case _ => throw new NoSuchMethodException() + } + def invoke(method: String, context: Context, arguments: AnyRef*) = callbacks.get(method) match { case Some(callback) => hosts(method) match { @@ -173,7 +178,7 @@ object Component { val a = m.getAnnotation[network.Callback](classOf[network.Callback]) val name = if (a.value != null && a.value.trim != "") a.value else m.getName if (!callbacks.contains(name)) { - callbacks += name -> new ComponentCallback(m, a.direct, a.limit) + callbacks += name -> new ComponentCallback(m, a.direct, a.limit, a.doc) } } ) @@ -201,11 +206,11 @@ object Component { // ----------------------------------------------------------------------- // - abstract class Callback(val direct: Boolean, val limit: Int) { + abstract class Callback(val direct: Boolean, val limit: Int, val doc: String = "") { def apply(instance: Environment, context: Context, args: Arguments): Array[AnyRef] } - class ComponentCallback(val method: Method, direct: Boolean, limit: Int) extends Callback(direct, limit) { + class ComponentCallback(val method: Method, direct: Boolean, limit: Int, doc: String) extends Callback(direct, limit, doc) { override def apply(instance: Environment, context: Context, args: Arguments) = try { method.invoke(instance, context, args).asInstanceOf[Array[AnyRef]] } catch {