Перейти к содержанию

Лидеры


Популярный контент

Показан контент с высокой репутацией за 11.12.2018 во всех областях

  1. 14 баллов
    Ну что ж. Буквально на днях майнось наконец релизнулась в виде полностью самостоятельной и независимой от OpenOS операционки с собственным набором библиотек и вики. Все родное, отечественное (звучит как диагноз). Большинство методов библиотек по типу filesystem или keyboard крайне схожи с таковыми в опеноси по поведению, так что особых проблем с переходом возникать не должно. Также существенно снизился расход оперативной памяти - примерно на 7% от 4 планок 3 уровня. Осталось, конечно, регулярно выискивать мелкие недочеты и допиливать их, а также более подробно наполнять инфу на вики, но в целом результатом я доволен. Среди ключевых нововведений стоит отметить следующие: Полностью переписанный инсталлер, запускающийся даже с EEPROM, имеющий возможность выбора тома для установки и его форматирования, а также систему конфигурации пользовательского профиля. Добавлено несколько системных языков, а заодно возможность установки лишь выбранного языкового пакета вместо всех сразу для экономии места на диске: Полная двойная буферизация графики, все приложения переписаны под библиотеку GUI. Кстати, местный картинко-редактор заимел оконный режим, а также Ёлочка @Totoro стала отлично работать в фоне и украшать хату (в моем случае - жалкий клочок земли в воздухе): Крайне полезный режим Internet Recovery, позволяющий мгновенно переустановить систему напрямую из EEPROM в случае возникновения каких-либо проблем: Возможность заливки файлов напрямую на Pastebin буквально парой кликов: Фича создания ассоциаций расширений файлов с возможностью назначения приложения для открытия того или иного расширения:
  2. 9 баллов
    Я очень обожаю дронов из OC, довольно быстрые и манёвренные(и дешёвые!). Меня смущает насколько автофермы из разных модов дорогие, так что вот решение этой проблемы: https://pastebin.com/9TBqRHPw (Для ленивых и счастливых обладателей интернет-карты: pastebin get 9TBqRHPw dronefarm.lua) (Проверьте, работает ли у вас сбор культуры на ПКМ, иначе же работать не будет(Версия на ЛКМ была, но она неудобная)) В программе всего 4 настройки: Размер фермы по X и Z Ожидание в секундах(Это нужно для того, чтобы культура успела вырасти) Ожидание при зарядке(Если дрон разрядился во время полёта) Процент заряда, при котором дрон вернётся домой Конфигурация дрона совсем простая, нужен лишь инвентарь(Можно и два инвентаря, так будет лучше). Установка довольно простая: Скачиваем ферму Делаем чистый EEPROM Вставляем в комплюхтер Пишем flash -q dronefarm.lua dronefarm(Не обязательно dronefarm, можно что угодно написать) Вставляем в дрона ??? Профит! Расстановка фермы: Где жёлтый - граница фермы Красный - место стоянки Длина и ширина может быть любой, главное чтобы зарядки хватило(Если у дрона переполниться инвентарь или разрядиться до 20% от общей зарядки единиц - он вернётся на базу)
  3. 9 баллов
    Показывает основную информацию о системе и дисках, позволяет выбирать приоритетный и конкретные диски для загрузки. Из работающего: Информация о системе Краткая информация о дисках Загрузка через init.lua в корне диска Установка приоритетного загрузочного диска (при старте биос будет пытаться загрузится именно с него, а потом уже проверять другие диски) Выжигающий глаза интерфейс и код Форматирование дисков Загрузка в MineOS Из не работающего: Загрузка в Plan9k и SecureOS Вкладка настроек Частично работающее: Загрузка в MineOS (не будет работать если установлено значение приоритетного диска) Предстоит реализовать: Вкладку настроек с языком Включение/выключение авторана Функцию ремонта файла запуска системы (перекачка с репозиции) Модификатор запускаторов MineOS и OpenLoader Запуск файлов на уровне биоса Поддержку (хоть какую нибудь) дисков в блочном режиме Вырезать главный кусочек и сделать возможность работы без интернет карты Перелопатить интерфейс и код, дабы делать всё через ООП Для установки прошейте биос этим файлом: https://raw.githubusercontent.com/titan123023/OCBios/master/bios-starter.lua Сслыль на репозицию: https://github.com/titan123023/OCBios Для работы требуется: Интернет карта Монитор любого уровня Клавиатура (если хотите зайти в сам биос) Как минимум 1 палец (см описание выше)
  4. 8 баллов
    Система предоставляет графическую оболочку для планшетов, имеющую минималистичный интерфейс и понятное только мне использование, а так же минимальное (надеюсь) потребление ОЗУ. Из фич оболочка дает: Возможность использования OpenOS частично без использования команд. Для особых случаев - используем контекстное меню -> "Выполнить команду" Возможность посылки уведомлений пользователю. Многозадачность не реализована, так что пассивную часть программы нужно активировать библиотекой thread из OpenOS Запуск программ-папок (*.pkg). Чисто для разграничения кода и возможности создания модулей Адаптивная отрисовка интерфейса. На экранах с разрешением по ширине, не кратной 20, могут возникать проблемы, однако без искусственного изменения разрешения такого не произойдет. Помощь в настройке при первом запуске. На случай проблем - на первом экране используется колёсико мыши. Блокировка экрана Горячие клавиши на главном экране (клик+delete - удалить, ctrl+e+клик - редактировать и подобное) QR-коды для быстрого доступа юзера к ссылкам В планах: Специальный фреймворк аля Zygote из андроида. Естественно абсолютно весь функционал переписывать не буду, однако основной останется. Этот фреймворк повлечет за собой полный рефакторинг кода (перевод системы на него), но полностью устранит все недостатки TabletOSNetwork - что бы было. Протокол сам в себе будет держать защиту от MITM (Сначала на DSA, потом переведу на ECDSA (реально сложно для меня пока)) и некоторую маршрутизацию с помощью специальных реле (что бы у юзеров планшеты не лагали). Установка - pastebin run 1xudmTa7 Выберите в установщике TabletOS и канал обновлений "Stable". В дальнейшем система будет уведомлять о обновлениях, при получении оного нужно будет зайти в настройки (контекстное меню в левом нижнем углу экрана) и там обновиться. В случае, когда при обновлении бросает ошибку - посмотрите изменения, там будут инструкции по ручному обновлению или переустановке системы. Если и это невозможно. переустановите систему. Данные должны сохраниться, а вот система - обновиться.
  5. 8 баллов
    Видел я на хабре пост четырёхлетней давности о включении лампы(в реальном мире) из майнкрафта. Вот он, если что: https://habr.com/ru/post/271527/ Тут я подумал - "А чем я собственно хуже? Почему нельзя контроллировать температуру реактора/дроноферму/дом датчиками движением?" Переходим к практике. Я имею вот такой дисплей купленный в поднебесной(За всеми ссылками и подробной помощью если заинтересуетесь обращаться в личку) Так же, я обладаю платой NodeMcu Lolin V3(Если грубо - это ардуинка с вай-файкой, но нет, это самостоятельный чип esp8266) В общем это всё, что нам сейчас понадобится(Еще понадобится роутер (и белый айпи, если вы играете на сервере)) Я хочу контролировать температуру своего реактора из IC2(но пристроить можно всё что угодно), поэтому берём его. Как это выглядит в игре: Схема реактора(Хотя ничего особо интересного тут нет): Переходим к реальному миру. Нам нужно открыть порт, в моём случае это 4536, к тому же нам нужен постоянный адрес в интернете. Для этого я использую DDNS. Об этом и остальном можно узнать в гугле. Теперь, всё готово чтобы наконец-то смотреть температуру реактора из реально мира! Есть такой простой код(Сейчас говорим про реальный мир): --Если кому будет интересно, вот полная документация по прошивке NodeMcu: https://nodemcu.readthedocs.io/en/master/ wifi.sta.config({ssid = "Xiaomi_2.4G", pwd = "xBfYHR33Le"}) --Мой SSID и пароль моего wi-fi =D wifi.sta.autoconnect(1) --Автоподключение Wi-Fi wifi.setmode(wifi.STATION) --Сейчас ESP8266 Будет работать в режиме клиента) spi.setup(1, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, 8, 8) --Настройка дисплея oled = u8g2.ssd1309_128x64_noname0(1, 8, 4, 0) oled:setFont(u8g2.font_haxrcorp4089_t_cyrillic) --Выставляем шрифт oled:setContrast(255) --Контрастность дисплея) server = net.createServer(net.TCP, 30) --Создаём сервер с тайм-аутом в 30 секунд по TCP протоколу function receiver(socket, temp) --Функция, которая вызывается при подключении oled:clearBuffer() --Очищаем буфер oled:drawUTF8(1, 7, "Темп. Реактора: " .. temp .. "%") --Пишем oled:sendBuffer() --Уже рисуем socket:close() --Разрываем подключение end server:listen(4536 , function(conn) --Выставляем на прослушку порт 4536 conn:on("receive", receiver) end) wifi_check = tmr.create() --Создаём динамический объект таймера wifi_check:alarm(1000, 1, function() --Эта функция - аналог event.timer в OpenComputers, которая так же вызывает функцию. В данном случае мы каждую секунду проверяем Wi-Fi. if not wifi.sta.getip() then --Если мы не имеем айпи, тогда wifi_connect = false oled:clearBuffer() --Очищаем буфер oled:drawUTF8(1, 7, "Подключение к Wi-Fi...") --Пишем oled:sendBuffer() --Рисуем elseif not wifi_connect then --Если мы только что подключились и не получили температуру реактора, тогда рисуем просто текст без температуры wifi_connect = true oled:clearBuffer() --Очищаем буфер oled:drawUTF8(1, 7, "Темп. Реактора: ожидание...") --Пишем oled:sendBuffer() --Рисуем end end) Код в майнкрафте(Очень простой): local component = require("component") local internet = require("internet") local reactor = component.reactor while true do local connection = internet.open("ip", 4536) connection:write(string.sub(reactor.getHeat(), 1, 2)) connection:close() os.sleep(5) end Теперь посмотрим что получилось! Подключение к Wi-Fi: Ожидание температуры: Итог: В общем то тут довольно всё криво сделано, тут надо много чего доделать, на самом деле. Но в целом, работает же=) Мне кажется, что это очень круто(Жаль что всё это без чанклодеров так себе работает). Такие дела. P. s @cyber01 Я сидел в редакторе долго, появилась ошибка "Произошла ошибка конфигурации или сервера". Пришлось вручную редактировать всё=(
  6. 8 баллов
    Обновление завез. Фиксы: Пимпочка скроллбара неправильно отображалась (окно с прокруткой сразу после отрисовки отображало пимпочку, несоотвествующую реальным размерам показанного контента) Юникод в поле ввода теперь поддерживается полноценно Папка рабочего стола изменена в связи с грамматической ошибкой (Desctop -> Desktop) Добавлено: Горячие клавиши. Клик + delete - файл удаляется, клик + ctrl + e - редактирование, клик+ctrl+e+delete - перезапись файла. Иконка батарейки в статусбаре - . (К сожалению, пока является статичным элементом) Техническое: файл настроек автоматически обновляется Изменено: Техническое: очистка кода в SetupWizard Техническое: частичный рефакторинг TabletOSGraphics. P.s. стараюсь выпускать обновления в первую субботу каждого месяца. До этого все изменения накапливаются в канале Experimental. В инсталлере этот канал есть, можете смотреть). Но там обычно немного нестабильно.
  7. 8 баллов
    Всем привет! Накануне я решил создать свой полноценный Банк, но пока я его делал, решил, что это будет карта на прохождение и специально допустил пару уязвимостей. (На самом деле я решил сделать карту на прохождение потому, что мне было лень их фиксить :d). Суть карты в том, что необходимо взломать банк и получить 10000$ на банковской карте. Уязвимости довольно скрыты и предстоит помучаться чтобы их найти, уязвимостей всего две. Первая - глобальная, средней сложности. Вторая - очень скрытая, высокая сложность. На карте так же реализовано подобие вышки, которое позволяет отправлять пакеты друг другу не напрямую, а через эту вышку. Так-же эта вышка помогает покрыть этим "интернетом" всю карту (необходимо поставить много вышек по всей карте) и преодолеть ограничение радиуса отправления пакетов. Банковская карта реализована RFID меткой. На карте присутствует банкомат, сервера, ваш домик и немного налички в сундуке. Правила карты (На вашу совесть, чтобы было интересно): Запрещается использовать компьютеры, кроме своего. (Просматривать логи сервера банка и сервера вышки на экране разрешено). Запрещается просмотр исходников серверов. Запрещается ломать блоки. (Карта в режиме приключения, но читы включены) Так-же имеется библиотека на вашем компьютере для работы с внутренним интернетом, находится она по пути: /lib/gsm.lua Строения на карте: 1. Вышка интернета. 2. ДЦ Сбербанк. 3. Офис Сбербанк. (Компьютер не трогать в нём) 4. Банкомат. 5. Домик кулхацкера. Подсказки по прохождению карты можно попросить у меня во ВКонтакте: https://vk.com/superrolan51 Ссылка на небольшой ролик по карте: Клик Ссылка на сборку: Клик Ссылка на портативную сборку от @Asior: Клик (Для версии Forge 1.7.10) Удачи в взломе! P.S. Не пишите в комментариях подсказки к карте
  8. 8 баллов
    По этой теме уже существует отлаженный софт, успешно используемый многими игроками. Однако на днях я заметил забавную особенность местных видеокарт, позволяющую выставлять разрешение большее, нежели получаемое через gpu.maxResolution(). К примеру, если maxResolution для видеокарты третьего уровня вернет числа 160 и 50, то никто не мешает установить разрешение, скажем, в 20x158 пикселей. При этом при проверке валидности устанавливаемого разрешения соблюдается два правила: Результирующее разрешение по числу пикселей не должно превышать результат умножения чисел, возвращаемых maxResolution. То есть для T3 GPU не более 160 * 50 = 8000. Каждый параметр разрешения, будь то ширина или высота, не должен численно превышать значение возвращаемой ширины. То есть для T3 GPU не более 160. Не знаю, баг это или фича, однако подобное грех не использовать в своих целях. К примеру, старая версия программы при вертикально-удлиненном расположении мониторов позволяла выставить разрешение лишь в 30х50 (1500 пикселей в итоге), заполняя тем самым "черные полосы". В то же время обновленная софтина, учитывающая описанные выше особенности, выдает 69x114 (7866 пикселей), максимально приближаясь к предельной отметке в 8000: Согласитесь, лишние пиксели на дороге не валяются, и, думаю, кому-то будет интересно узнать про реализацию подобного софта. Прежде всего нам требуется определить точную пропорцию мультиблочного монитора: то, как относится его ширина к высоте. Взглянем на текстуру блока: она явно состоит из мелких "квадратиков" в количестве 16 штук, причем лицевая часть монитора имеет рамку толщиной в 2 "квадратика" с каждой стороны: Эти "квадратики" мы и будем использовать для расчета пропорции, так как они позволяют идеально точно получить размеры отображающей части монитора в игровом мире. А размеры "в квадратиках" можно посчитать по формуле: число_блоков_монитора * 16 - 4 Следовательно, пропорция монитора будет считается следующим образом: local gpu = component.gpu local screen = component.proxy(gpu.getScreen()) local blockCountByWidth, blockCountByHeight = component.screen.getAspectRatio() local proportion = (blockCountByWidth * 16 - 4) / (blockCountByHeight * 16 - 4) Также не забываем, что высота каждого псевдографического пикселя при отображении в 2 раза больше его ширины, поэтому формула пропорции слегка меняется. Заодно произведем некоторые сокращения, чтобы избавиться от лишних математических операций. Все три варианта эквивалентны: local proportion = 2 * (blockCountByWidth * 16 - 4) / (blockCountByHeight * 16 - 4) local proportion = (blockCountByWidth * 16 - 4) / (blockCountByHeight * 8 - 2) local proportion = (blockCountByWidth * 2 - 0.5) / (blockCountByHeight - 0.25) После вычисления пропорции монитора можно приступить к написанию программной логики. Для начала получим максимальное разрешение видеокарты: local maxWidth, maxHeight = gpu.maxResolution() Обладая этими данными, а также взглянув на условия, описанные в начале поста, мы можем составить систему неравенств для получения итогового идеального разрешения: Помним также, что ширину мы вполне можем выразить через высоту и пропорцию монитора: width = proportion * height Подставим этот вариант в систему: Оставим высоту в левой части неравенств: Подставляем имеющиеся переменные в каждое неравенство, рисуем числовую прямую и выбираем высоту, удовлетворяющую условиям неравенства для конкретного случая. Разумеется, в программировании никаких числовых прямых нет, зато есть любимые math.min() и math.max(): local height = math.min( maxWidth / proportion, maxWidth, math.sqrt(maxWidth * maxHeight / proportion) ) -- Выражаем ширину через пропорцию local width = height * proportion Все. Разумеется, полученные значения ширины и высоты нужно округлить в меньшую сторону, дабы не кормить видеокарту дробями. Заодно добавим поддержку масштабирования, чтобы выставлять половинное или, скажем, четвертичное от идеального разрешение. Сократив код, избавившись от лишних переменных, мы получаем следующее: local component = require("component") -- Получаем масштаб в качестве первого аргумента скрипта и корректируем его значение local scale = tonumber(select(1, ...) or 1) if not scale or scale > 1 then scale = 1 elseif scale < 0.1 then scale = 0.1 end local gpu = component.gpu local blockCountByWidth, blockCountByHeight = component.proxy(gpu.getScreen()).getAspectRatio() local maxWidth, maxHeight = gpu.maxResolution() local proportion = (blockCountByWidth * 2 - 0.5) / (blockCountByHeight - 0.25) local height = scale * math.min( maxWidth / proportion, maxWidth, math.sqrt(maxWidth * maxHeight / proportion) ) -- Выставляем полученное разрешение gpu.setResolution(math.floor(height * proportion), math.floor(height)) Надеюсь, это микро-знание кому-то было полезно. Лично я очень доволен, что могу наконец запилить графонистый интерфейс для контроля реакторов на вертикальных мониках без осваивания профессии "глиномес", да и соответствующая либа для автопобора разрешения в оське пригодится.
  9. 7 баллов
    Всем здрасти. Скрины: Клиент 1: Клиент 2: Лог сервера: Реализовано: Выдача IP Передача данных по выданым IP Регистрация/удаление доменов Получение IP адреса по домену То что хочется реализовать: Улучшить защиту, возможно сделать шифрование -Улучшить способ передачи данных между внутриигровыми IP Сделать библиотеку для пользовательских программ(С++, Java, Python, итп) для управления\получения данных OC через сервер, а так же поддержку Arduino(Можно будет выводить значения на экран подключеный к Arduino) Добавить поддержку децентрализации если в сети более 1 сервера(Можно будет делать межсерверные DNS-запросы) (Продолжение следует) Плюсы: Можно соединять OC компьютеры стоящие на разных MC серверах Быстрая работа сервера Минусы: Нужна оптимизация и поиск багов Плохая защита Исходники GitHub: https://github.com/TheConnBit/OpenComputersDHCP-DNS P.S. Там же объяснение всех команд, итп Исходники для ленивых (обновлено): Сервер (Запускать через консоль java -jar и лучше в отдельной папке): DHCP-DNS-Server.jar Клиентская библиотека: ddns.lua На скрине показано: подключение к серверу, выдача IP, регистрация домена, запрос IP домена, передача данных, получение, удаление домена, отключение Вообщем, сильно не пинайте за код. Если кто возьмется искать\найдёт баги или будут предложения что улучшить\добавить, пишите мне сюда или на почту bithovalsky@gmail.com Сначало была идея только DHCP сервера, но потом я накнулся на статью Programmist135: http://computercraft.ru/topic/1853-dns/ и реализовал DNS. Запущен DHCP-DNS сервер: 185.224.135.134:9999 Последние изменения: - - v1.1 Исправлены названия функций Исправлен перевод Функция Resolve теперь возвращает IP Пофикшены отключения сервером клиентов из за таймаута Echo запроса Список доменов теперь указывается в файле config.properties Изменён способ отправки сообщений по IP, появились порты --Bit
  10. 6 баллов
    Представляю вам опять программу для робота, которая позволяет добывать руду, не лазая по пещерам. Робот, используя геолизер, может самостоятельно находить и добывать руду. Реализованы еще не все возможности, поэтому прошу тестировать и сообщать мне о багах. Требования: Корпус компьютера (уровень II или III) Апгрейд инвентарь (больше - лучше) Апгрейд контроллер инвентаря Жесткий диск EEPROM с прошитым Lua BIOS Геосканер Память (уровень I или выше) Процессор (любой) Апгрейд полета (I уровень) Алмазная кирка или аналогичный инструмент. Опционально: Апгрейд верстак Беспроводная сетевая карта Апгрейд батарея Апгрейд опыта Апгрейд чанклоадер Апгрейд генератор Эндерсундук из мода EnderStorage Установка: Скачать и сохранить файл как init.lua wget https://raw.githubusercontent.com/DOOBW/geominer/master/miner.lua init.lua Закинуть этот файл в корень диска. Добавить диск при сборке робота. Установить робота на платформу из твердых блоков. Дать роботу кирку. Поставить возле робота контейнер и зарядник. Нажать кнопку питания и наслаждаться процессом.
  11. 6 баллов
    Тут на днях обновленная софтина Multiscreen в маркет затесалась - добавлена группировка по цветам для гораздо более быстрой отрисовки жирных пикч. Правда, чем жирнее пикча, тем дольше будет обрабатываться каждый моник, но и это уже заметный прогресс:
  12. 6 баллов
    Ой, забыл xD Спойлеры делать не умею. так что как-то так пока. Возможно на скриншоты слишком новые и из разрабатываемой версии.
  13. 6 баллов
    За полный сбор:
  14. 6 баллов
    Давненько не было автокрафтеров тут. Может, кому-то пригодится моя версия. Предназначена в первую очередь для крафта всяких часто необходимых мелочей со сложными крафтами (например, компоненты OpenComputers) из примитивных исходных ресурсов (например, процессор из слитков золота и железа, тростника и редстоуна), которые у пользователя отнимают кучу времени на поиск всех транзисторов и крафт недостающих. Рассчитана на использовании робота и двух сундуков, один из которых - основное хранилище (может быть покрупнее), а другой предназначен для резервирования компонентов при крафте. Ссылка: https://pastebin.com/1gqtWLub Необходимая конфигурация робота: Проверялась работа на компонентах (корпус, память, процессор, жёсткий диск) второго уровня. Screen, Keyboard Crafting Upgrade Inventory Upgrade и Inventory Controller Видеокарта и экран первого уровня Для дальнейших потенциальных расширений: Беспроводная карта Upgrade Container Конфигурация установки: Перед роботом - контейнер ресурсов (любой из возможных инвентарей достаточного размера) Под роботом - контейнер-буфер (можно обычный сундук) Рядом желательно поставить зарядник Возможности: Рекурсивный крафт сложных рецептов. Ресурсов расходуется, по результатам практических испытаний, ровно столько, сколько необходимо. Каталог рецептов для крафта, разбитый на страницы по 10 предметов для более удобного пролистывания на маленьком экране робота. Портативность - требуется только робот, два сундука и исходные ресурсы. Желателен также источник энергии Сообщения о том, каких конкретно исходных ресурсов не хватает для крафта (исходные ресурсы - те, для которых не найдено рецепта) Процесс крафта подробно отображается на экране, чтоб за ним было не так скучно и одиноко следить (см. недостатки) Об окончании крафта робот сообщит приветливым писком. Равно как и о неудаче. Недостатки и известные недочёты: Скорость..... Крафт занимает значительное время. Например, изготовление процессора 3го уровня из примитивных ресурсов (тростник, красный камень, алмазы, дерево для резаков, слитки золота, железа) занимает около 5 минут. Стоит отметить что количество изготавливаемых предметов не сильно влияет на время (2 процессора, скорее всего, будут делаться те же примерно 5 минут). Не умеет работать с альтернативными ресурсами. Возможно, когда-нибудь исправлю. Не умеет работать с инструментами (имеются ввиду многоразовые, как молот ИК2). Возможно, так же когда-нибудь исправлю. Не умеет работать с количествами предметов более стака, а также не гарантируется корректная работа с предметами, не складывающимися в стак. Постараюсь исправить в ближайшее время. Нет поиска по именам компонентов (то есть, либо задаёте название компонента целиком, либо задаёте крафт через каталог). Когда-нибудь поправлю Проверок на наличие контейнеров не делается, так как программа писалась "для себя" и находится в разработке. В дальнейшем будут введены. Также не везде гарантируется наличие защиты от "Количество предметов: Привет". Особенности: Шаблоны содержатся в одном файле, что облегчает переносимость, но приносит определённые неудобства всвязи с размерами файла (12 строк на предмет). Буду думать, как лучше сделать (разбить на разные файлы?). Файл имеющихся шаблонов могу выложить при необходимости (на разных сборках эти шаблоны могут отличаться) Дальнейшее развитие (no promises!): Исправление имеющихся недочётов Поддержка работы по сети (заказ компонентов, сообщение о готовности - дистанционно). Работа с более сложными инвентарями - сборщики, и т.д. Работа с машинами-обработчиками ресурсов - когда-нибудь в отдалённом будущем, скорее всего. Управление: Интерфейс текстовый. Посмотреть команды главного меню можно, нажав "Enter" (оставив поле ввода пустым).
  15. 5 баллов
    Не так давно решил я немного нафармить обсидиана, но прыгать по озерам лавы, тушить её водой, а потом долго и нудно собирать как-то не то. Хотелось автоматизировать этот процесс. Да есть специальные генераторы обсидиана, но там требуются расходники в виде красной пыли или другого горючего материала, что для меня было неприемлимо. Поэтому был собран первый прототип генератора обсидиана. Портатип генератора обсидианта Version 1.0 Довольно сложная но красивая конструкция. Время генерации 1 стака ~ 2 минуты 30 сек. Внешний вид: Инструкции по сборке: По времени работы меня такой результат вполне устраивал, но потом @Romanok2805 показал свою версию похожего генератора. Описывать его я не буду, поясню что смысл работы того генератора, он черпает ведром лаву из бочки, выливает её рядом с источником воды, меняет ведро на бур и разрушает обсидиан. И так по кругу. Обмозговав алгоритм его работы, был придуман улучшенный генератор обсидиана. Генератор обсидианта Version 2.0 Довольно легкая конструкция, дешевый робот. Время генерации 1 стака ~ 15 сек. Внешний вид: Инструкции по сборке: Генератор обсидиана Version 2.1 Довольно легкая конструкция, дорогой робот. Внешняя бочка не требуется. Время генерации 1 стака ~ 15 сек. Внешний вид: Инструкции по сборке: Генератор обсидиана промышленный Version 3.0 Дорогая конструкция, дорогой робот. Навешано чуть больше расчетов, в связи с чем время генерации обсидиана увеличено, но зато данный генератор может обновлять расходники. Время генерации 1 стака ~ 17 сек. Внешний вид: Инструкции по сборке: Собственно такие вот получились варианты сборки фермы обсидиана, я конечно хз зачем вам столько его потребуется, но, коль уж есть программа почему бы ею не воспользоваться. Всем удачи, копайте обсидиан быстрее, чем вам сосед P.S. Большое спасибо за демонстрацию и помощь в написании программы @Romanok2805, за информацию по багам @M_O_R_F_I_K
  16. 5 баллов
    Уважаемые форумчане и игроки проекта! Посовещавшись, администрация сайта пришла к выводу, что было бы неплохо и не лишним внести какие-то свежие весенние правки на сайте и в игровой процесс. Администрацией сайта был выработан план-капкан. Кодовое название операции "Весенний ветер" Сервер хоть и работает суперстабильно, но он устарел немного концептуально, и все построено и скрафчено, а сайт после осеннего обновления движка хоть и получился в целом достаточно функциональным, но немного визуально мрачноватый и даже слегка унылый. Ванильную светлую схему не рассматриваю. Не хватает какой-то привлекательности, не хватает игровой морды лица и живости, некоторые элементы сайта вообще выпадают из целостной картины и не вписываются в общую концепцию новой цветовой схемы и дизайна. Что видит пользователь, попав на главную страницу? Правильно. Он видит на так называемом "первом экране" название сайта в лого, огромную серо-черную портянку текста с непонятными зелеными и синими тайтлами и чат на полэкрана, в котором накопилось аж 10 сообщений за 1,5 месяца. Какие-то кнопки голосования и иконку глобуса, которая вообще не понятно что означает. То ли это кнопка интернета, а может быть это новости политики. Хотя это раздел писем и уведомлений. Почему там разработчик шаблона запихал не значок письма, или не колокольчик, а глобус, лично для меня до сих пор загадка. Но это еще мелочи. Бог с ним, с глобусом. Понять, что наш сайт в игровой форме обучает программированию, что на нашем проекте есть игровые сервера майнкрафт и опытные форумчане программисты, которые помогут легко влиться в игровой процесс и в изучение компьютерных модов и Lua и т.д., новому посетителю сайта очень сложно. Нет никаких четких заголовков о том, о чем вообще сайт. Нужно очень долго скроллить серую портянку текста вверх и вниз, чтобы понять(не знаю как кому, по крайней мере за себя говорю), что это вообще за херабра и что наш сайт делает и что на нем вообще находится и для чего он. В связи с этим в ближайшее время будут проведены следующие мероприятия: обновление движка до актуальной версии дизайн сайта будет изменен косметическая реструктуризация разделов форума и др. мелкие правки После выполнения вышеуказанных мероприятий администрация сайта приступит к развертыванию нового игрового сервера (о нем в отдельной теме) Оставляйте свои конструктивные предложения, референсы, ссылки на какие-то другие ресурсы, просьбы и т.п.. Проголосуйте в опросе, а также укажите в топике, что именно не нравится и что бесит, или если все хорошо и все нравится, тоже напишите. Что в данный момент сделано неудобно на сайте, что трудно найти, чего не хватает, может ссылок каких, картинок, разделов и т.д. и т.п. Например: "сделайте отдельную статическую Страницу-Шпаргалку Для Новичка, с подсказками, инфографикой, ссылками на полезные ресурсы и краткой информацией о том, что где и как скачать и изучать, как начать изучать моды и Луа, какая литература поможет, видеобзоры полезные может какие-то и т.п.. Кнопку ИГРАТЬ запихайте в значок какой-то на видное место в шапке, а ссылки на голосовалки никому не нужные поместите в футер" и в таком духе и т.п., главное, чтобы идеи были максимально конкретными и хоть как-то обоснованными и реально выполнимыми. Если темная схема окажется востребованной, тогда сделаем светлую и абсолютную копию в темном стиле. То есть сайт будет отличаться только цветом. Не будет как сейчас две разных верстки. Цель операции "Весенний ветер" - как-то немного освежить проект и сайт в частности, сделать его каким-то более удобным и приветливым и более игровым, что-то добавить новенькое, а также запустить новый сервер с какими-то новыми разнообразными фишками.
  17. 5 баллов
    И снова доброго времени суток! Написал и недавно допили прогу по упровлению светом с помощью RedLogic! Команда для скачивания: pastebin get -f uZFEL62c light.lua Вот код: component = require("component"); side = require("sides"); color = require("colors"); rs = component.redstone; --funcs------------------------------------------------------------------------------------------------------------- function hlp() os.execute('clear'); check(); print('\n-------------------------------------------------', '\nlight_1_on - to switсh on a light_1;', '\nlight_1_off - to switсh off a light_1;', '\nlight_2_on - to switсh on a light_2;', '\nlight_2_off - to switсh of a light_2;', '\nall_on - switсh on all light;', '\nall_off - to switсh off all light;', '\n-------------------------------------------------\n'); end -- function check() local component = require('component'); local light_1 = component.redstone.getBundledInput(2, 14); local light_2 = component.redstone.getBundledInput(2, 4); if light_1 <= 255 and light_1 > 15 then print('Light_1 \x1b[32m++\x1b[0m '); elseif light_1 >= 0 and light_1 < 15 then print('Light_1 \x1b[31m--\x1b[0m '); end if light_2 <= 255 and light_2 > 15 then print('Light_2 \x1b[32m++\x1b[0m'); elseif light_2 >= 0 and light_2 < 15 then print('Light_2 \x1b[31m--\x1b[0m'); end end -- function _light_1_on() rs.setBundledOutput(2, 14, 255); os.execute('clear'); check(); end -- function _light_1_off() rs.setBundledOutput(2, 14, 0); os.execute('clear'); check(); end -- function _light_2_on() rs.setBundledOutput(2, 4, 255); os.execute('clear'); check(); end -- function _light_2_off() rs.setBundledOutput(2, 4, 0); os.execute('clear'); check(); end -- function _all_off() rs.setBundledOutput(2, 4, 0); rs.setBundledOutput(2, 14, 0); os.execute('clear'); check(); end -- function _all_on() rs.setBundledOutput(2, 4, 255); rs.setBundledOutput(2, 14, 255); os.execute('clear'); check(); end --main-------------------------------------------------------------------------------------------------------------- os.execute('clear'); print('Enter \'help\''); local cmds = { light_1_on = _light_1_on, light_1_off = _light_1_off, light_2_on = _light_2_on, light_2_off = _light_2_off, all_on = _all_on, all_off = _all_off, help = hlp } local term = require("term"); while true do term.write('> '); cmd = term.read(); if cmd == false then print("Exiting!") os.exit() end cmd, _ = cmd:gsub("\n",""); if type(cmds[cmd]) == "function" then cmds[cmd](); else print("No such command: " .. cmd); end end (На лампе №1 нет контакта, но и так понятно что всё работает) Если преодолею лень и перестану тупить то возможно у этой проги появится GUI!
  18. 5 баллов
    Текстурпаки, музыка .... вы о чем народ! Очнитесь! Текстурпак каждый ставит на свой вкус ляд и производительность своего калькулятора. Я лично вообще ни одного текстурпака не ставил, так как и так не тянет. Нужно просто решить какие моды новые поставить, что оставить, что убрать, высказать идеи, историю мира, как то захватить игроков, чтобы им было интересно поиграть. Насчет приватов, я тут щас завис на одном проекте, где вообще вайпов нету, там реализована система регенирации мира с обходом приватов. Я хз как это реализовано, но это работает уже много лет. Если кто то забрасывает игру, то через n-ное время во время регена его приват удаляется и регенится в соответствии со всеми правилами. Конечно в этом случае есть опасность, что придет добрый Вася с командой роботов и за ночь выкопает котлован вокруг привата не понравившегося ему соседа, но это уже другой вопрос. Итак мое предложение по модам: ОС и все аддоны которые к нему есть (кое какие надо уже обновить) ИС2 заменить на Механизм (идея та же но функционал различный, + есть частичная интеграция с ОС) Магию убрать (интеграция с ОС никакущая и если честно то если ставится магия, то с технологией она как то не очень вяжется) Развитие обязательно через тинкерс (классный мод частичная интеграция с ОС) Раилкрафт (можно использовать из него чанклодеры личные, да и железку протянуть иногда возникает желание у всех) Еще можно было бы какие нибудь моды на оружие, турельки, пушечки Мод на агрессивных мобов. Новых мобов, не ванильных. По миру: Постапокалипсис. Мир серый и унылый, изредка встречаться будут леса, полянки, можно добавить какие нибудь структуры, острова которые можно разбирать и "сдавать на металлолом" в магазин. Время от времени на игроков обрушиваются какие то не очень приятные эффекты (радиоактивный ветер, комары гигантские, атака диких кобанов и т.д). Соответственно на спавне некий магазинчик, где можно затариться всем необходимым. Ах да, команда /afk или что то похожее, чтобы пока игрок под её действием его не донимали отрицательные эффекты, атаки и прочее (например когда ты сидишь с роботами которые копают ресы ~2 часов чтобы на тебя никто не агрился, ведь мало кто эти 2 часа сидит у компа и постоянно мониторит текущее состояние дел) это можно будет докупать в магазине. Но это лично мое предположение по новому серверу. Можно как то его еще обыграть, но я лично такой вариант вижу. Просто поймите, админы тоже люди, и генерировать идеи они не могут вечно. Так что давайте им поможем, выскажем свои варианты чего вы хотите видеть, сделаем хоть какие то наброски, а потом админы выберут самое интересное и реализуют. А лучше всего предложат на голосование несколько типов сборок и выберем то что понравится.
  19. 5 баллов
    Робот может двигаться, пора добавить функцию сканирования породы и калибровки компаса. (Пока тестировал, обнаружил баг работы с зачарованными инструментами, пришлось немного переделать функцию step() - теперь после неудачного свинга, робот дополнительно проверяет наличие блока. Можно будет оставить, даже когда разрабы это исправят) Чтобы отфильтровать блоки по плотности, надо получить плотность нужных блоков с учетом шумов. На расстоянии x8 z8 y1 от геосканера, максимальная плотность бедрока равна -0.317, внесем в фильтр -0.31. Для руды 3.683, но это ванильная руда, в модах бывает и больше. Минимальная плотность обсидиана 49.312, значит, eсли он не нужен, установим для полезных блоков максимальную плотность 40. C минимальной плотностью не все так гладко. Свинцовая руда из индастриала имеет плотность 2.5 это как у деревянных предметов, разброс с учетом шума от 1.3 до 2.7, это пересекается с камнем, у которого 0.8 - 2.2. Вот таблица некоторых блоков с минимальной и максимальной плотностью: Руда 2.312 - 3.683 Стекло -0.388 - 0.983 Камень 0.812 - 2.183 Грязь -0.188 - 1.183 Сундук 1.312 - 2.683 Обсидиан 49.312 - 50.683 Видно, что плотность стекла пересекается с плотностью коренной породы, но у бедрока приоритет выше, поэтому лучше лишний раз обойти. Исходя из этих данных, полезные блоки будут отмечаться с минимальной плотностью 2.3 и максимальной 40 Теперь опишем функцию сканирования. Заглянем в подсказку. Чтобы получить сырые данные, зададим координаты и размеры квадрата, относительно сканера. geolyzer.scan(позиция_х, позиция_z, позиция_y, ширина, длина, высота) Так как один раз можно отканировать только 64 блока, будем делать 4 подхода, получая координаты квадрата по горизонтали из вызывающей функции. Преобразовываем данные в координаты, попутно анализируя плотность условным оператором и устанавливаем метки. При обнаружении бедрока устанавливаем соответсвующий флаг во внешней для всех функций переменной. Получаем функцию scan(), выглядеть она будет примерно так: local function scan(xx, zz) -- сканирование квадрата x8 относительно робота local raw, index = geolyzer.scan(xx, zz, -1, 8, 8, 1), 1 -- получить сырые данные, установить индекс в начало таблицы for z = zz, zz+7 do -- развертка данных по z for x = xx, xx+7 do -- развертка данных по х if raw[index] >= 2.3 and raw[index] <= 40 then -- если обнаружен блок с плотностью от 2.3 до 40 table.insert(WORLD.x, X+x) --| записать метку в список table.insert(WORLD.y, Y-1) --| с коррекцией локальных table.insert(WORLD.z, Z+z) --| координат геосканера elseif raw[index] < -0.31 then -- если обнаружен блок с отрицательной плотностью border = true -- сделать отметку end index = index + 1 -- переход к следующему индексу сырых даннх end end end Раз уже взялись за геосканер, напишем и компас. Чтобы определить стороны света, надо сломать блок перед носом, просканировать его, затем установить обратно и, если есть разница - выдать результат. Для большей надежности добавим вращение вокруг своей оси, т. к. блока перед носом может и не быть или быть, но не тот. Координаты блоков задаем в таблице, сбрасываем текущее направление, определяем заново, вот и вся функция. Назовем ее compass() local function compass() -- определение сторон света local sides = {{-1,0}, {0,-1}, {1,0}, [0]={0,1}} -- привязка значений сторон света к смежным блокам D = nil -- обнуление текущего направления while not D do -- пока направление не найдено for n = 0, 3 do -- перебор сторон света robot.swing(3) -- разрушение блока if geolyzer.scan(sides[n][1], sides[n][2], 0, 1, 1, 1)[1] == 0 and robot.place(3) then -- тестовое сканирование и установка блока if geolyzer.scan(sides[n][1], sides[n][2], 0, 1, 1, 1)[1] > 0 then -- если обнаружена разница в сканах D = n -- установить новое направление break -- выйти из цикла end end end turn() -- задействовать простой поворот end end Самые важные функции готовы, можно приступить к тестированию.
  20. 5 баллов
    Ядро копателя готово, теперь можно и пощупать. Напишем пробную функцию сканирования и добычи одного слоя. Сначала откалибруем компас и зададим таблицу с координатами сканируемых квадратов. Затем, отсканируем квадрат 16 на 16 блоков, выведем количество обнаруженных блоков. И в цикле обойдем все метки. Вроде бы все просто. Ах, да... будем искать ближайший блок к текущей позиции, чтобы быстрее закончить работу. Есть много подходов к определению расстояний. Например квадрат гипотенузы равен сумме квадратов катетов, формула для нашего случая будет math.sqrt((X-x)^2+(Z-z)^2), где X,Z - координаты робота, x,z - координаты метки, можно выкинуть квадратный корень, в нашем случае бесполезный и даже вредный. Но тут есть одно "но", мы получили гипотенузу, а это наименьшее расстояние между точками, а роботы по диагонали не ходят. Я буду вычислять дельту между точками, суммируя реальное расстояние, которое пройдет робот по формуле math.abs(X-x)+math.abs(Z-z) Эта операция в сферическом вакууме потребляет на 5% больше процессорного времени, чем предыдущая, но с лихвой окупается сэкономленными шагами. В цикле будем обходить таблицу с метками, до каждой вычисляя расстояние, самый лучший результат с индексом будем хранить в отдельных переменных. По окончании работы цикла, будем посылать робота в ближайшую точку. Код всей тестовой программы под спойлером. А вот и видео с демонстрацией. Можно добавить штрафы на повороты, тогда он будет меньше крутиться и собирать кучи линейкой, а не змейкой.
  21. 5 баллов
    Недавнo завезли нескoлькo нoвых функций. canSeeSky():boolean - вoзвращает true, если виднo небo. Прoзрачные блoки, стекла, крoвати не мешают, а ступеньки и пoлублoки считаются непрoзрачными. detect(стoрoна:number):boolean, string - кoпия функции component.robot, прoверяет наличие блoка с указаннoй стoрoны. Вoзмoжные варианты выдачи: (false, 'air') - пустo, блoк вoздуха. (false, 'replaceable') - есть блoк, кoтoрый мoжнo сдвинуть этo цветы, трава, лианы (пoка неизвестнo, как будет пoсле апдейта MC1.14). В приватах мoжнo считать за true, т. к. рoбoтoм лoмаются тoлькo кактусы. (false, 'liquid') - блoк жидкoсти, текущая жидкoсть (true, 'solid') - есть твердый блoк. (true, 'entity') - какая-тo сущнoсть. (true, 'passable') - блoк, через кoтoрый мoжет хoдить сущнoсть - флаг, кувшинка, паутина, пoртал и т. п. Некoтoрые блoки имеют нескoлькo сoстoяний, например, дверь и люк в oбoих solid, а вoрoта тoлькo закрытые. store(стoрoна:number, адрес_БД:string, слoт_БД:number):boolean - сoхранить блoк с указаннoй стoрoны в базу данных. isSunVisible():boolean - вoзвращает true, если пoгoда ясная, вo время дoждя или грoзы - false
  22. 5 баллов
    А я костылял си апи в луа для опенкомпов. Нет, плюсы даже я костылять не стал — мне и си хватило. Ситуация была такова. Я тогда ещё пилил свою гуи-либу-убийцу-всего-живого-своей-охрененностью, и она как-то подозрительно тормозила. А когда программы подозрительно тормозят, юзают профилятор. Который желательно писать на си, дабы минимизировать задержки. Что я и сделал, после чего последовали долгие недели попыток прицепить си-модуль к луа на OC. Ну, прежде всего, я чутка пропатчил мод: внёс внутрь песочницуы дебаг-либу родную и не менее родной модуль package, который умеет запускать си-модули. Затем я всяко-разно пытался скомпилировать свой профилятор так, что при загрузке его он подхватывал бы символы луа. Не получилось. Поэтому я взял сырцы луа, которые юзаются в OC. Я их взял, всунул туда свой профилятор и сцомпилировал их вместе так, будто профилятор был встроенной либой луа. Костыли страшные, да. Получившимся подменил то, что лежало стандартно внутри мода, и пересобрал последний. Такими шаманствами я таки осилил получить свой ненаглядный профилятор внутри опенкомпа, и потому смог пропрофилировать свою гуи-либу-убийцу-всего-живого-своей-охрененностью. Это мне, конечно, ни о чём не поведало новом и ничем не помогло, да и гуи-либу эту я таки забросил, но это уже другая история. А можно без костылей? Не, без костылей не получится. Не потому, что плюсы — это уже костыль. Хотя это тоже справедливо (то есть, нет, конечно, не будем холивар разводить). А потому, что авторам мода и в голову не приходило, что кто-то чем-то подобным будет заниматься, и, более того, намеренно всяко-разно отрезали куски луа так, чтобы это оказалось в принципе невозможным. Потому как всем известно, как просто в си выстрелить в ногу вне зависимости от желаний владельца этой ноги, что недопустимо на публичных серверах всяких. Поэтому остаётся учить Луа. Язык это хороший, простой, минималистичный, красивый и полезный, хотя и чутка неудобный порою, как минимум, для заядлого заплюсневевшего программиста из-за динамической типизации.
  23. 5 баллов
    @Totoro зараза! Ну что ж... вряд ли автор объявится, так что пусть она будет Тоторина, ибо так велит мой бог, нашептывая на ушко. Касаемо оси: кардинально переработана система полноэкранных приложений. При активации режима полного экрана для каждого окна полностью отключается весь интерфейс, за исключением верхней менюхи. Таким образом в полноэкранном режиме приложения пашут с той же скоростью, как если бы они были не зависимы от ОСи. А еще добавлена клевая анимация изменения размеров окон:
  24. 5 баллов
    Бывали ли у вас когда-нибудь такие ситуации, когда необходимо защитить какую-либо таблицу от записи? Например, вы пишите операционную систему в OpenComputers, и хотите хоть какую-то защиту от вирусов. Бедные таблицы всегда в очень большой опасности. С tprotect ваши таблицы будут в безопасности, так как другие программы вместо оригинала таблицы получат её зеркальную копию. А когда вы захотите снова в нее что-то записать, вы сможете это сделать (оригинал же у вас?). Вообщем вот ее код : А теперь документация, а то я пишу код непонятно: function tprotect.protect(table:t):table -- Возвращает "зеркало" таблицы t и её метатаблицу (у "зеркальной" таблицы она неизменяема). "Зеркальная" таблица защищена от записи и всегда является отражением t. Если вы хотите защитить таблицу t, то на публичное (доступное для других программ, от которых вам хотелось бы защитить t) место, где вы ставите обычно таблицу t, ставьте "зеркальную" таблицу
  25. 5 баллов
    Я сделал еще в октябре(31 числа), на форуме чет затишье, решил выложить
  26. 5 баллов
    Предлагаю своё решение. local f = io.open("/etc/program.cfg", "r") local content = f:read("*a") f:close() local cfg = {} assert(load(content, "=program.cfg", "t", cfg))() print(cfg.player) Соответствующий конфиг будет выглядеть тогда так: player = "Robot" robot = "Player" age = 39 alive = false С синтаксисом Луа. Что, согласитесь, выглядит приятно. И не надо запятые ставить, как в таблице сериализованной.
  27. 5 баллов
    во первых, чтобы была конкуретность, а во вторых MineOS это не ось, а граф.оболочка. Эта фраза уже бичом нервов стала, а чертовы доморощенные эксперты-казуисты продолжают лезть изо всех дыр. Элементарная ОСь на платформе опенкомпов состоит буквально из нескольких сот строк кода, реализующих методы работы с файловой системой, позволяющих монтировать физические дисковые носители, грузить библиотеки по предустановленным путям, обрабатывать события и имитировать многозадачность. Все остальное - это графическая оболочка, в моем случае состоящая из десятка тысяч строк кода, вылизанных до предела, чтобы уложиться в строгие рамки ресурсов, выделенных под каждую виртуальную машину в кубаче. Интерфейс майноси с базовым прикладным софтом по типу проводника/настроек/пикчредакторов/магазинов/IDE мы писали с большой командой товарищей около четырех с фигом лет, постоянно его совершенствуя. Эксперимента ради я решил отвязать майнось от опеноси, сделав ее полностью независимой. Это заняло несколько часов. Ценой нескольких, мать их, часов, а также нескольких написанных либ суммарным объемом в две сотни строк майнось стала полностью удовлетворять определению "операционная система", загружаясь с очищенного от скверны жесткого диска и собственного EEPROM. Результат лежит на отдельном репозитории, законсервированный до лучших дней: https://github.com/IgorTimofeev/MineOSStandalone А теперь скажи мне, умник, не кажется ли тебе странным, что вся "ось" пишется на коленке за несколько часов, а "граф. оболочка" - пускай и не годами, но в значительно большие сроки? Может быть, термин "ось" переоценен, и основу современной интерфейсной операционной системы составляет все же графическая оболочка c прикладным софтом? Эх, мяу
  28. 5 баллов
    Кстати о майноси: тут несколько занятных софтин подъехало. Например, YobaКалькулатрон9000, как его окрестил товарищ-шестиклассник. Умеет почти все: от работы с различными системами счисления и побитового редактирования чисел до базовых бинарно-тригонометрических операций с отображением символа по его коду. Во-вторых, слегка обновился местный проводник, заимев удобную панель навигации по текущему пути с кликабельными подпунктами а-ля Винда. Ну, и всякие кнопочки для ручного ввода пути появились, а также фича ресайза тулбара: Ну, и наконец не шибко сложная, однако довольно полезная в быту возможность интерфейсного привата компа по аналогии с useradd:
  29. 5 баллов
    Программка почти не изменилась, просто использует другой тип графика. Конечно тут речи никакой не идет о тонкой линии которая будет чертить график, я использовал то что есть в уже готовой библиотеке. Для работы программы все так-же требуется адаптер. К требованиям компьютера изменений нет, все так-же что запихаешь то и будет работать. Единственное что поменял, это теперь график не имеет никаких ограничений по размерам экрана. Да и вообще никакая информация кроме графика больше не рисуется. Проверка на изменения количества ресурсов в бочке проводится раз в секунду, если изменений нет, график не отрисовывается. График показывается следующим образом: Берется максимально допустимое количество ресурсов, что можно запихать в бочку, и делится пополам. Все что выше половины то отрисовывается зеленым, если уровень ресурсов падает ниже середины, то отрисовка проходит красным. Установка программы в 2 шага: Загрузка библиотеки: pastebin get -f wchQZtZB /lib/charts.lua Загрузка программы: pastebin get -f zjiKv5F0 Graf_drawer.lua
  30. 4 балла
    В этой печали сложно быть одиноким. В Интернете ничего не понявших большинство. Подавляющее. На этом форуме достаточно и тех, кто что-то понимает. И если верно задать вопрос, то кто-то из них даже согласится дать пояснения. Поэтому я сразу предлагаю перейти к основному вопросу: Что именно ты желаешь понять?
  31. 4 балла
    Пошли в личку вопросы о Recurrent Complex, что-то не работает, краш, так как не найдена либа и пр. Чтобы этот вопрос закрыть раз и навсегда по оформлению декором и данжами наших серверов, напишу микрогайд. В общем для работы нужно: ванильный майн сам мод последней версии для майна 1.7.10, https://minecraft.curseforge.com/projects/recurrent-complex , там же документация и вика библа IvToolkit-1.2.1.jar MineTweaker3-1.7.10-3.0.10B.jar для удобства получения форж имен блоков и айтемов желание, мышка и клава (всё это запихать в папку модс) /#export сохранить в файл структуру /#gen NewStructure ~ ~ генерировать структуру NewStructure /#confirm подтвердить генерацию/импорт /#edit NewStructure ~ ~ редактирование настроек структуры NewStructure /#import NewStructure импорт структуры для редактирования NewStructure /#fill minecraft:dirt заполнить выделенное землёй /#replace minecraft:air reccomplex:negativeSpace заменить блоки воздуха на кубики Для того чтобы мод не генерировал свои кастомные постройки измените в: ...\minecraft\config\reccomplex.cfg S:structureGenerationMatcher=!$reccomplex Команды МайнТвикер: /mt hand что у меня в руке? Копирует в буфер обмена. Папки, где находяться активные постройки и инвентари: ...minecraft\structures\active Предлагаю массово включится в работу и понастроить миллион домиков, разрушенных дотов, башенок, катакомб, вышек, может даже микро кусочки городских кварталов, подземных командных центров и прочих построек в качестве данжей и антуража. Если каждый заинтересованный форумчанин или игрок построит хотя бы по одной небольшой структурке и импортнет это в файлик и всё это мы запихнем потом в папку генератора, у нас вместо унылого ванильного ландшафта майнерского будет мир, наполненный некогда бурной человеческой деятельностью, но после ядерных ударов по всему миру будут по легенде валятся упавшие небоскребы и какие-то катакомбы и разрушенные города и поселки, а под землей масса тоннелей и комнаток повстанцев и очагов сопротивления с пышным лутом, компами и монстриками. Вот референсы, в каком стиле это рекомендуется выполнить. Только гигантизма не нужно. Пускай это будет отдельно стоящая башенка заросшая лианами, домик с оторванной крышей, кусочек автострады и тд. и тп. Предпочтение будет отдано реалистичным постройкам, а не машинкам с колесами из шерсти. Машинок НЕ нужно ни в коем случае бутафорских детских, как на дейзи серверах. Только каменно-бетонные объекты из ванила блоков. Важно!!!! Постройки, хоть сурфейсные хоть подземные, должны аккуратно вписываться в ландшафт, а для этого нужно использовать вспомогательные блоки реплейсеры (аир блоки из мода, негатив блоки, натурал флоры для подошвы сооружения и пр.) Постройка, которая сетит возле себя квадратный куб воздуха, хоть подземная хоть наземная, будет скорее всего проигнорирована и не включена в натурал-генератор мира сервера. Лучшие постройки попадут на страничку сервера в карусельку скринов и даже может самые четкие и интересные постройки попадут в слайдер. И будет автор потом любоваться своим творением. Ну и призы конечно всем активным участникам на новом сервере в виде реса, эпической брони и легендарного оружия, которое расщепляет все живое на атомы, и куча боеприпасов и робот будет выдан из мода ОС!
  32. 4 балла
    Добавил две новые реакции: Одобряю и Против. Зачем, возможно, спросите вы? А для того, что эти две реакции в большей мере отражают ваше отношение к суждениям других форумчан. Всякие радостные и грустные мордочки, сердечко - это чисто эмоции. Понравилась ли вам картинка, программка, анекдот и т.п. - можно поставить смайлик или сердечко. Ну и самое главное - больший спектр и больший выбор реакций и эмоций. А для более серьезных обсуждений требуются иные реакции. В профиле форумчанина накопилось много смешных реакций - ага, форумчанин юморист, например, а большое кол-во сердечек показывает качество контента. А вот если игрок на форуме активно участвует в холиварах, предлагает дельные или спорные идеи, получает реакции поддержки или наоборот, его предложениям и суждениям ставят реакцию против - это совсем другое. И это тоже должно отражаться в репутации. Например, хочет админ понерфить солярки в каком-то посте мимолетном, а тут бац, и миллион реакций против прилетело. И даже и голосовать не надо. Или какой-то игрок предлагает установить на сервер покемонов, например. Тоже можно эту реакцию заюзать. В данный момент это чисто визуальная реакция. Ее кол-во накапливается в профиле полученных реакций, но на уровень репутации пока не влияет. И у меня возник вопрос, нужно ли этой реакции сделать (-1) репутации? Проголосуйте, пожалуйста, в опросе.
  33. 4 балла
    Давным-давно делал модный файловый менеджер с графическим интерфейсом для опенкомпов. Переходы по папкам, запуск файлов, распаковака tarball'ов и просмотр картинок в одной программе, к тому же фичи в виде листания свайпами, экранной клавиатуры и горстки настроек. И все это добро занимало меньше килобайта. Но развивать идею не стал, код удалил и осталась только одна картинка тестовой версии. Недавно решил это дело возродить, без зависимостей и лишних свистоплясок. Для начала напишем функции, которые добавят дополнительные возможности для пользователя. Когда игрок тыкает в экран, создаются два события - touch и drop. Когда зажимает и тащит - touch, потом куча drag и в конце drop. Из имеющихся событий, можно развить дополнительные события - клик, двойной клик и свайп. Можно даже добавить сложные жесты, но пока не понятно, как они могут пригодиться. На все нужные события повесим слушателей и будем сохранять результат в переменную. Слушатель для события touch будет проверять, было ли предыдущее событие drop. Затем сравнит с временем от последнего клика, вычислит расстояние между точками, в которых произошло событие. При совпадении координат и заданным временем между кликами пошлет событие double_click. Для события drop надо проверить, было ли предыдущим touch и по тому же параметру скорости проверять время между событиями, чтобы не захватывать долгие нажатия. Если предыдущим событием было drag, то надо определить расстояние между началом и концом действия, вычислить угол и послать это все в виде события swipe. В итоге получится примерно такой код: local computer = require('computer') -- подгрузить обертку для uptime & pushSignal local event = require('event') -- подгрузить библиотеку событий local lastEvent = nil -- последнее действие local lastTouch = nil -- последнее касание local eventTime = nil -- время от последнего события local clickSpeed = 0.5 -- время, за которое совершается клик и дабл-клик event.listen('drag', function(...) lastEvent = {...} -- просто сохранить событие end) event.listen('touch', function(...) local e = {...} -- сохранить событие в таблицу if e[5] == 0 and lastEvent and lastEvent[1] == 'drop' then -- если нажата ЛКМ и предыдущее было drop if eventTime and computer.uptime()-eventTime < clickSpeed then -- если прошло меньше времени, чем задано if lastTouch and lastTouch[3]-e[3]+lastTouch[4]-e[4] == 0 then -- если координаты событий не отличаются computer.pushSignal('double_click', e[2], e[3], e[4], e[6]) -- послать дабл-клик с координатами end end lastTouch = e -- сохранить последнее касание end eventTime = computer.uptime() -- обновить таймштамп события lastEvent = e -- сохранить событие end) event.listen('drop', function(...) local e = {...} -- сохранить событие в таблицу if e[5] == 0 and lastEvent then -- если нажата ЛКМ if lastEvent[1] == 'touch' then -- если предыдущее событие было касанием if eventTime and computer.uptime()-eventTime < clickSpeed then -- если прошло меньше времени, чем задано computer.pushSignal('click', e[2], e[3], e[4], e[6]) -- послать клик с координатами end elseif lastEvent[1] == 'drag' then -- если предыдущее было тасканием local dx, dy = lastTouch[3]-e[3], lastTouch[4]-e[4] -- найти дельту до координат касания computer.pushSignal('swipe', e[2], dx, dy, math.floor(math.deg(math.atan(dx/dy))), e[6]) -- послать свайп с дельтой и углом end end eventTime = computer.uptime() -- обновить таймштамп события lastEvent = e -- сохранить событие end) Пока он ничего не делает, только создает события, когда будет готов функционал отрисовки и взаимодействия с файловой системой, добавим к этим слушателям управляющие функции.
  34. 4 балла
    Еще одно обновление. Удалил из TODO свайпы - слишком сложно и бесполезно. Добавил в TODO QR-коды для ссылок - очень просто (нашел либу и научился ей пользоваться) и полезно - с сервиса_нейм ко мне в соцсеть_нейм и в мессенджер_нейм будут (надеюсь) быстрее перетекать юзеры и писать багрепорты, которые я сам не обнаруживаю. Фиксы: Ассоциации файлов не работали вообще Теперь "низкий уровень свободной памяти" - 64 килобайта. При меньших значениях возникали проблемы. Всплывающие окна теперь располагаются правильно. Добавлено: Подсказки пользователю о некоторых аспектах работы с системой (решил перевести все на нормальный фреймворк вместо однотипных кусков кода по всей оболочке). Кнопка "Продолжить" у поля ввода. Кнопка удаления уведомления, а так же при клике по самому уведомлению оно откроется полностью. У файлов в "Все программы" появилось контекстное меню. Изменения: Полный рефакторинг системы обновлений. Внимание: вам придет два обновления. Одно обновление обновит оболочку, а второе - инициализирует систему обновлений.
  35. 4 балла
    В процессе подгонки всех функций программа очень быстро разрослась. Дам краткий обзор всех новинок. Минимальная и максимальная плотность были вынесены в начало программы к остальным переменным. + переменная port, для модема. + переменные steps и turns, вначале для отладки. steps пригодилась для подсчета шагов, чтобы каждые 32 шага проверять состояние инструмента и батареи. + функция arr2a_arr() - преобразование списков в ассоциативный массив для быстрого доступа к элементам. Массивы tails, fragments преобразуются этой функцией, функции работающие с ними, адаптированны соответствующим образом. + функция report(), позволяющая передавать пользователю статусные сообщения посредством модема или связанной карты. В данный момент еще пикает и выводит сообщения на экран. Также завершает работу программы, при получении соответствующего параметра. remove_point() отделилась от функций, удаляющих метки из таблицы. Функция check() отделилась от функции step(). Каждые 32 шага или по принуждению вычисляет расстояние до стартовой точки, сохраняет текущие координаты. Если уровень энергии или инструмента не хватает на дорогу до дома + 64 шага, совершается переход домой, потом возврат к работе. Если есть генератор, происходит попытка заправки. Каждый шаг проверяются точки вокруг робота и удаляются, если робот может их достать. Функция calibration() объединилась с калибровкой компаса. Происходит проверка компонентов: геосканер, контроллер инвентаря, инструмент и наличие блока под роботом, в случае не обнаружения - программа завершается. Настраиваются модем или связанная карта. sorter() упростилась и ускорилась. + функция home() перемещает робота на точку старта, запускает сортировку и упаковку предметов, ищет сундук (требует, в случае не обнаружения). Складывает добычу в сундук, в случае переполнения ждет, когда освободится место. Достает из сундука неупакованные предметы и переупаковывает. Забирает из сундука стак с углем, при наличии генератора. Кроме перехода к точке старта, ищет в инвентаре эндерсундук из EnderStorage и сгружает ресурсы в него. Пытается найти более новый инструмент в сундуке, либо ищет зарядник и пробует засунуть в него и зарядить. На точке старта ждет, пока уровень энергии не достигнет 99% потом возвращается к работе. + функция main() - основной цикл сканирования и добычи. Робот сканирует весь чанк, в цикле перебирает все метки, выбирает ближайшую и перемещается к ней, пока не пройдет по всем. Ну и цикл перехода по спирали от чанка к чанку. Запускает функцию main(), по завершении вычисляет координаты следующего чанка, перемещает к нему, запускает main() и т. д., пока не дойдет до последнего, потом вызывает функуию home() и завершает работу. Осталось добавить возвращение по хлебным крошкам при аварии. И правильную работу с инструментами, имеющими нестандартную механику износа - энергетические и магические кирки/буры из модов. Еще можно заставить робота таскать с собой генератор из какого-нибудь мода и зарядник, тогда ему не нужно будет возвращаться на точку старта для подзарядки (а с эндерсундуком вообще на надо будет возвращаться) Бета-версию можно посмотреть и скачать тут - https://pastebin.com/hXWLDKre
  36. 4 балла
    Недавно начал писать бота с использованием этой замечательной библиотеки, но к несчастью столкнулся с некоторыми недоработками. Поэтому решено было доработать библиотеку imodem и вот что из этого получилось: Обновление для библиотеки imodem Теперь imodem может обрабатывать личные сообщения (раньше просто игнорил их). Говорим спасибо @Bit Добавлена авторизация ника Все данные по именам/каналам лежат в файле конфигурации по адресу /etc/imodem.cfg Возможен запуск по данным пользователя из Shell Добавлено логирование Добавлено переподключение если отключится интернет Итак, поясняю. Немного изменился порядок приема сообщения. Теперь входные данные выглядят так: modem_message ник_imodem ник_говорящего 0 #канал/ник_imodem сообщение Если сообщение идет с канала, то указывается канал, если личное сообщение, то ник imodem. Чтобы не лазить по библиотеке, ради изменения ника, канала, сервера было решено перенести все эти данные в файл конфигурации. Библиотека автоматически проверяет файл конфигурации, и в случае если его не найдет, самостоятельно его восстановит. Данные хранятся в следующем виде: nick = "Тут ник" pass = "Пароль (0)" server = "irc.esper.net:6667" channel = "#канал" log = "Записывать логи (1/0)" Чтобы не заморачиваться с настройкой ника/канала/сервера/логов можно передать все эти данные одной командой: imodem [server[:port]] <nickname> <password> <channel> <save(1/0)> <log(1/0)> Ну а если все настроено и вас устраивает, можете напрямую imodem запускать и по сохраненным данным все запустится. Логирование сделано больше для отладки, логи можно найти по адресу /IMODEMLOG.txt Для того чтобы imodem мог переподключаться, через каждые 60 секунд оправляется пингование на сервер. И если в течении 30 секунд не придет ответ, imodem автоматически переподключится. Это лучше чем словить кратковременный сбой в доступе к интернету и потом вручную перезагружать библиотеку. Скачать библиотеку можно по команде pastebin get -f MvR7FTGS /lib/imodem.lua Выражаю благодарность за помощь в улучшении @Bit, @Doob, @vladimir, @Fingercomp
  37. 4 балла
    Чтобы программа могла контролировать движения робота, добавим систему координат и функционал связанный с ней. Так как робот будет шахтером, то все движения должны сопровождаться разрушением блоков, он будет ползать сквозь породу, попутно захватывая руду. Описание основной двигательной деятельности занимает всего четыре функции (можно и три, но в прошлой версии, в процессе борьбы за место, пришлось одну разделить) Приведу базовый код, затем опишу, что он делает. local component = require('component') -- подгрузить обертку из OpenOS local X, Y, Z, D = 0, 0, 0, 0 local WORLD = {x = {}, y = {}, z = {}} local function add_component(name) -- получение прокси компонента name = component.list(name)() -- получить адрес по имени if name then -- если есть адрес return component.proxy(name) -- вернуть прокси end end local robot = add_component('robot') -- загрузка компонента local function step(side) -- функция движения на 1 блок local state, type = robot.swing(side) -- тестовый свинг if not state and type == 'block' then -- если блок нельзя разрушить print('bedrock') os.exit() -- временная заглушка else while robot.swing(side) do end -- копать пока возможно end if robot.move(side) then -- если робот сдвинулся, обновить координаты if side == 0 then Y = Y-1 elseif side == 1 then Y = Y+1 elseif side == 3 then if D == 0 then Z = Z+1 elseif D == 1 then X = X-1 elseif D == 2 then Z = Z-1 else X = X+1 end end end if #WORLD.x ~= 0 then -- если таблица меток не пуста for i = 1, #WORLD.x do -- пройти по всем позициям if X == WORLD.x[i] and (Y-1 <= WORLD.y[i] and Y+1 >= WORLD.y[i]) and Z == WORLD.z[i] then if WORLD.y[i] == Y+1 then -- добыть блок сверху, если есть robot.swing(1) elseif WORLD.y[i] == Y-1 then -- добыть блок снизу robot.swing(0) end table.remove(WORLD.x, i) -- удалить метку из таблицы table.remove(WORLD.y, i) table.remove(WORLD.z, i) end end end end local function turn(side) -- поворот в сторону side = side or false if robot.turn(side) then -- если робот повернулся, обновить переменную направления if side then D = (D+1)%4 else D = (D-1)%4 end end end local function smart_turn(side) -- поворот в определенную сторону света while D ~= side do turn((side-D)%4==1) end end local function go(x, y, z) -- переход по указанным координатам while Y ~= y do if Y < y then step(1) elseif Y > y then step(0) end end if X < x then smart_turn(3) elseif X > x then smart_turn(1) end while X ~= x do step(3) end if Z < z then smart_turn(0) elseif Z > z then smart_turn(2) end while Z ~= z do step(3) end end Сначала создаются переменные для локальных координат робота. X, Y, Z - собственно, позиция робота, относительно стартовой точки. D - направление, куда смотрит мордочка робота. при старте программы она относительная. Поэтому, чтобы привязать ее к сторонам света, надо будет произвести некоторое шаманство при помощи геосканера. Таблица WORLD - это метки, которые будут устанавливаться в процессе сканирования. Таблица разделена на три, это смежные хранилища переменных для каждой координаты, например, сканер обнаружил блок с подходящей плотностью по координатам x15, y-10, z3, в таблицу они будут добавлены по одному индексу. Допустим, таблица была пустая, после добавления будет иметь вид WORLD.x[1] = 15, WORLD.y[1] = -10, WORLD.z[1] = 3 или WORLD = {x = {15}, y = {-10}, z = {3}} Далее следует функция, упрощающая добавление компонентов. На вход получает имя нужного компонента и, если он есть, выдает прокси к нему. Функция step() - основное движение робота. Учитывая, что программа используется исключительно для копания, копание будет в каждом шаге. Робот не тыкается носом в породу и не спрашивает какой блок перед ним. Махнул инструментом и смотрит результат. Если есть блок, но добыть его не получилось - следовательно, дальше делать нечего, там бедрок или еще чего похуже, потом добавим правильную обработку и эвакуацию по хлебным крошкам, а пока пусть будет заглушка. Если махнул удачно - пробуем еще раз и еще, до посинения. Это своеобразная защита от лагающего гравия/песка и назойливых сущностей (в виде гномиков). Далее, если функция движения была совершена удачно, то обновляем локальные координаты, учитывая направление движения. Ну и в конце функции сканируем таблицу меток, ищем метки с текущей позицией и удаляем, т. к. по нашим данным робот находится в блоке руды, следовательно, он его добыл. Еще есть дополнительная проверка по вертикали - если есть руда сверху или снизу, то захватываем, это позволит сократить общую сумму переходов между метками и ускорить добычу. Функция turn() - основной поворотник (аналог robot.turn(), но с обновлением переменной направления) Робот поворачивается, записывая результат в переменную, добавляя/отнимая единицу по модулю 4 при каждом повороте. Функция smart_turn() - поворот на желаемую сторону света, с минимумом действий. Вычисляет разницу между текущим и целевым направлением, запуская результат по модулю 4 через turn() Функции поворота можно будет объединить, но пока оставлю так. Функция go() - великий ход конем до нужных координат. Принимает координаты целевого блока, двигается по вертикали, поворачивает на цель X, двигается до цели, поворачивает на цель Z, двигается до цели. Для поворота использует smart_turn(), т. к. оси x и z глобальные, это стороны света.
  38. 4 балла
    И так, наконец-то возвращаюсь к роботу-копателю. Да будут новые баги и новые фичи! Краткий план внедряемых фич: Улучшенное сканирование руд. Робот сканирует под собой квадрат 16x16 блоков, опускаясь блок за блоком. При обнаружении бедрока запускается функция добычи. При добыче робот поднимается и в цикле ищет ближайшие по горизонтали блоки руды, захватывая три слоя - Y+1, Y, Y-1 Определение энергопотребления сборки при запуске. На старте, робот запоминает количество потребленной энергии на один шаг + прочность инструмента. Это будет служить константой при проверке статуса, чтобы была возможность гарантированно вернуться на точку старта. Умная упаковка добычи. Перед обработкой рассыпухи, теперь будет точнее анализироваться свободное место, упаковка не будет происходить механическим перебором, из-за которого бывали внезапные сбои. При наличии генератора, робот всегда будет с собой таскать уголь, при разгрузке на точке старта будет забирать стак угля или угольных блоков. Текущие константа энергопотребления и координаты будут записываться в EEPROM. Следовательно, при наличии сетевой/связанной карты, робота можно будет будить и не бояться выгрузки чанков, лагов, космических лучей. Скорее всего, добавлю функцию аварийного крафта кирок из булыги, в случае работы с ванильными инструментами. Планируется утилита, собирающая программу по параметрам, заданным пользователем. ...Или не планируется, скорее всего все возможности копалки не получится впихнуть на EEPROM, поэтому на EEPROM будет загрузчик с main функцией, а дополнительные модули придется записывать на жесткий диск. Планируется поддержка модов, например, возможность возить с собой и разворачивать заправочную станцию в виде генератора и зарядника. Или скидывать предметы в межпространственные сундуки. Тут надо будет смотреть, как будут развиваться моды. В блоге буду описывать каждую функцию, чтобы отследить создание программы шаг за шагом, надеюсь, кому-то это поможет.
  39. 4 балла
    Часто необходимо писать программы для серверов. Это могут быть сервера для чатов, или файловые облака или что то еще, но всегда приходится писать велосипеды. Подумав об этом я решил написать программу для сервера. Программа работает просто. Подгружает модули из папки проекта, а потом начинает слушать все event'ы и обрабатывать. Представляю вам саму программу: Servercore v0.2.1 Исходный код: http://pastebin.com/NASX9sX0 Использование: Создаем папку проекта Создаем в этой папке файл .servercore Запускаем servercore указывая первым аргументом папку которую мы создали. Если не указывать аргумент то servercore запустится в рабочей директории. Наслаждаемся рабочим сервером, который пока ничего не делает. Любые файлы (кроме .servercore) которые находятся в папке проекта будут загружены как модули. Если модуль содержит ошибку наш сервер не полетит, а просто выведет тест ошибки на экран. При создании модулей можно использовать специальные функции, которые находятся в _G.sc. Описание этих функций: sc.info(info_type:string, message:string) - выводит информацию со временем и раскрашивает как на скринах. Принимает тип информации и само сообщение. Типов информации всего 4: ok,err,warn и info. sc.getTime() - возвращает время в формате unix timestamp. sc.on(event_name:string,handler:function) - добавляет слушателя на сигнал. Циклом слушаются все сигналы а потом запускают функцию обработчик для того сигнала который пришел. Пример простейшего модуля: sc.on("touch",function (e) sc.info("info","you touched!")end) Изменения в версии: Патч 1: убрана обязательная поддержка модемов. Старые версии: Произведение «Servercore» созданное автором по имени LeshaInc, публикуется на условиях лицензии Creative Commons «Attribution-NonCommercial-NoDerivatives» («Атрибуция — Некоммерческое использование — Без производных произведений») 4.0 Всемирная. PS: Название проги звучит как поджанр метала)))
  40. 4 балла
    К примеру в Java код, который мы пишем - оптимизируется во время компилирования и переводится в байткод. Этот байткод выполняется виртуальной машиной java. У каждого проца свой набор команд и чтобы выполнить одну и ту же операцию - надо использовать разные команды. Код под Intel не будет рабоатть на ARM или AMD. Нам бы приходилось переписывать/компилировать свою программу для каждого вида процов. Поэтому сделали промежуточную прогу, которая выполняет то, что написано в наших прогах, байткод это просто оптимизированный и упрощенный вид наших прог, разбитый на кучу мелких простых операций.
  41. 4 балла
    О, самая сложная часть уже осилена? Ну, что сказать, все кактусы это взрывает напрочь. Буду следить — но не беспокойтесь: дальше этой темы слежку проводить не стану.
  42. 4 балла
    Если используется OpenOS. Все четыре варианта более-менее схожи: require("shell").execute("/Test.lua") os.execute("/Test.lua") dofile("/Test.lua") -- Более детальное решение с безопасным запуском файла и отлавливанием всех ошибок с подробной информацией local result, reason = loadfile("/Test.lua") if result then result, reason = xpcall(result, debug.traceback) if result then -- Все замищательно else print("Не удалось выполнить файл: ", reason) end else print("Не удалось загрузить файл: ", reason) end Если используются "чистые" опенкомпы на микроконтроллере или попросту с EEPROM: local filesystem = component.proxy(component.list("filesystem")()) -- Читаем файл напрямую с диска local handle = filesystem.open("/Test.lua", "rb") local data, chunk = "" repeat chunk = filesystem.read(handle, 4096) data = data .. (chunk or "") until not chunk filesystem.close(handle) -- Выполняем прочитанный исходный код local result, reason = load(data) if result then result, reason = xpcall(result, debug.traceback) if result then -- Усе в порядке, файл выполнен else -- Ошибка при выполнении загруженного скрипта end else -- Ошибка при загрузке текстовых данных в качестве кода end
  43. 4 балла
    Так, программка готова. Рекомендую размер экрана 3х2. Хотя вроде должно нормально работать и на экранах большего размера. Рядом (сверху/снизу/неважно короче) с бочкой надо поставить адаптер. Его подключить к компьютеру. По устройству компьютера требований вообще никаких, что запихаешь то и будет. Установка программы в 2 шага: Загрузка библиотеки: pastebin get -f wchQZtZB /lib/charts.lua Загрузка программы: pastebin get -f mSRmMS72 Graf_drawer.lua Рекомендую вручную вбивать команды, а то при копировании лезет дополнительная информация в буфер обмена и ОС начинает глючить. Ну и скрин, как это выглядит: Обновляется программа каждую 1 секунду, но график меняется только при изменении количества предметов, что там заложены. Название предмета, если ты играешь на сервере может выводиться на каком-то другом языке (зависит от настроек сервера)
  44. 3 балла
    Добытые ресурсы надобно рассортировать. Для этого дела задействуем контроллер инвентаря. Надобно пройти по всем слотам, получить информацию о содержимом и сравнить название со списком ненужных предметов (который предварительно составим), при совпадении опустошать. Но это не вся функция. У нас есть еще верстак, который может помочь, очень сильно ужать, некоторые ресурсы (уголь, редстоун, алмазы, изумруды, лазурит). Верстак занимает в инвентаре 9 слотов, еще 1 слот добавим на результат, если не все влезет в блок. Поэтому, пока робот ищет мусор, пусть считает пустые слоты, для верстака. В начале уберем блоки сверху и снизу, чтобы случайно не перемешать мусор с излишками добра. для удобоваримости сократил некоторые имена: inventory - размер инвентаря, получим в начале работы программы, controller - контроллер инвентаря, tails - список названий лишних предметов (без префикса "minecraft:", можно добавлять названия из любого мода) robot.swing(0) -- освободить место для мусора robot.swing(1) -- освободить место для буфера ------- сброс мусора ------- local empty = 0 -- создать счетчик пустых слотов for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for name = 1, #tails do -- пройти по таблице хвостов if item.name:gsub('%g+:', '') == tails[name] then -- проверить на совпадение robot.select(slot) -- выбрать слот robot.drop(0) -- выбросить к отходам empty = empty + 1 -- обновить счетчик break -- прервать цикл сравнения end end else empty = empty + 1 -- обновить счетчик end end Далее следует проверить и выкинуть наверх предметы, которые будут мешать при крафте. Подсчитанные пустые слоты отнимем от требуемого количества для крафта, пройдем по инвентарю уберем их. -- упаковка предметов в блоки -- if crafting then -- если есть верстак -- перенос лишних предметов в буфер -- if empty < 10 then -- если пустых слотов меньше 10 empty = 10-empty -- увеличить количество пустых слотов для обратного отсчета for slot = 1, inventory do -- просканировать инвентарь if robot.count(slot) > 0 then -- если слот не пуст robot.select(slot) -- выбрать слот robot.drop(1) -- выбросить в буфер empty = empty - 1 -- обновить счетчик end if empty == 0 then -- если место освободилось break -- прервать цикл end end end Предварительно создадим таблицу fragments, в которой будут храниться названия предметов, которые можно сложить в блоки. Теперь создадим таблицу, в которой будут счетчики для каждого типа фрагментов. Пройдем по инвентарю, получим информацию о слоте, сравним, прибавим - все как в первом цикле, можно было бы даже их объединить, но на предыдущем шаге мы выкинули какие-то предметы. Чтобы узнать какие именно - придется городить еще один цикл, оставим как есть. -- подсчет предметов доступных для упаковки -- local available = {} -- создать таблицу счетчиков for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for n = 1, #fragments do -- пройти по списку названий фрагментов if item.name:gsub('%g+:', '') == fragments[n] then -- сравнить по имени if available[n] then -- если есть подобные фрагменты available[n] = available[n] + item.size -- обновить else -- иначе available[n] = item.size -- создать end break end end end end Наконец-то можно крафтить. Хотя, нет. Надо расчистить слоты верстака, чтобы в него сложить рецепт. Будем перебирать слоты от 1 до 9, но в роботе верстак занимает слоты с другими номерами, а именно 1 2 3 5 6 7 9 10 11, можно было бы составить условие от 1 до 11, с исключением целых по модулю 4. Сделаем проще - номера слотов занесем в таблицу "workbench", которая будет служить списком ссылок с системы 1-9 на 1-11. Вынесем ее подальше, чтобы она не создавалась при каждом запуске. В цикле проверяем количество предметов в слоте, если оно не нулевое - ищем в инвентаре пустой слот, исключая слоты верстака. Переносим предметы в найденный слот. Если перенести не удалось - что-то попало в верстак после крафта. Забираем предметы из буфера и завершаем функцию, возвращая true, что будет сообщать о перегрузе и времени выдвигаться домой. for c_slot = 1, 9 do -- цикл чистки зоны верстака if robot.count(workbench[c_slot]) > 0 then -- если слот не пуст robot.select(workbench[c_slot]) -- выбрать слот верстака for slot = 4, inventory do -- обойти весь инвентарь, кроме рабочей зоны if robot.count(slot) == 0 and (slot == 4 or slot == 8 or slot > 11) then -- если есть свободный robot.transferTo(slot) -- освободить слот break -- выйти из цикла end end if robot.count() > 0 then -- проверить на перегрузку robot.suck(1) -- забрать из буфера return true -- остановить упаковку end end end Верстак расчищен, пора заняться упаковкой. Перебираем слоты инвентаря, исключая верстак, сравниваем названия со списком. При совпадении, делим содержимое на 9, заполняем верстак И крафтим блок. ------- основной цикл крафта ------- for slot = 4, inventory do -- цикл поиска фрагментов local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item and (slot == 4 or slot == 8 or slot > 11) then -- если есть предмет вне рабочей зоны if item.name:gsub('%g+:', '') == fragments[i] then -- сравнить по названию фрагмента robot.select(slot) -- при совпадении выбрать слот for n = 1, 9 do -- цикл заполнения рабочей зоны robot.transferTo(workbench[n], item.size/9) -- разделить текущий стак на 9 частей и перенести в верстак end if robot.count(1) == 64 then -- сброс при заполнении верстака break end end end end crafting.craft() -- создание блока Можно заметить fragments, откуда i? Об этом позже. После крафта могли остаться какие-то остатки, если не все слоты поделились на 9. Проверяем содержимое в слотах, если предметов меньше 64 - перебираем слоты после текущего и сравниваем, при совпадении содержимого пробуем перенести. При опустошении текущего слота - прерываем перебор со сравнением. -- цикл сортировки остатков for A = 1, inventory do -- основной проход local size = robot.count(A) -- получить количество предметов if size > 0 and size < 64 then -- если слот не пуст и не полон robot.select(A) -- выбрать слот for B = A+1, inventory do -- проход сравнения if robot.compareTo(B) then -- если предметы одинаковые robot.transferTo(B, 64-robot.count(B)) -- перенести до заполнения end if robot.count() == 0 then -- если слот освободился break -- прервать сравнение end end end end Последние три цикла заворачиваем в такую конструкцию: for i = 1, #fragments do -- перебор всех названий if available[i] then -- если в инвентаре такой есть for j = 1, math.ceil(available[i]/576) do -- разделить результат на стаки ... end end end Первый цикл перебирает названия фрагментов. Условный оператор проверяет наличие такого типа в инвентаре. Внутренний цикл повторяет чистку, крафт и сортировку, если в результате будет больше стака блоков. Новые используемые переменные: local tails = {'cobblestone','dirt','gravel','sand','stained_hardened_clay','sandstone','stone','grass','end_stone','hardened_clay','mossy_cobblestone','planks','fence','torch','nether_brick','nether_brick_fence','nether_brick_stairs','netherrack','soul_sand'} local workbench = {1,2,3,5,6,7,9,10,11} local fragments = {'redstone','coal','dye','diamond','emerald'} local controller = add_component('inventory_controller') local crafting = add_component('crafting') local inventory = robot.inventorySize() Полный текст функции, с более рациональным вызовом robot.count(): local function sorter() -- сортировка лута robot.swing(0) -- освободить место для мусора robot.swing(1) -- освободить место для буфера ------- сброс мусора ------- local empty = 0 -- создать счетчик пустых слотов for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for name = 1, #tails do -- пройти по таблице хвостов if item.name:gsub('%g+:', '') == tails[name] then -- проверить на совпадение robot.select(slot) -- выбрать слот robot.drop(0) -- выбросить к отходам empty = empty + 1 -- обновить счетчик break -- прервать цикл сравнения end end else empty = empty + 1 -- обновить счетчик end end -- упаковка предметов в блоки -- if crafting and empty < 12 then -- если есть верстак и переполнение -- перенос лишних предметов в буфер -- if empty < 10 then -- если пустых слотов меньше 10 empty = 10-empty -- увеличить количество пустых слотов для обратного отсчета for slot = 1, inventory do -- просканировать инвентарь if robot.count(slot) > 0 then -- если слот не пуст robot.select(slot) -- выбрать слот robot.drop(1) -- выбросить в буфер empty = empty - 1 -- обновить счетчик end if empty == 0 then -- если место освободилось break -- прервать цикл end end end -- подсчет предметов доступных для упаковки -- local available = {} -- создать таблицу счетчиков for slot = 1, inventory do -- пройти по слотам инвентаря local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item then -- если есть предмет for n = 1, #fragments do -- пройти по списку названий фрагментов if item.name:gsub('%g+:', '') == fragments[n] then -- сравнить по имени if available[n] then -- если есть подобные фрагменты available[n] = available[n] + item.size -- обновить else -- иначе available[n] = item.size -- создать end break end end end end ------- основной цикл крафта ------- for i = 1, #fragments do -- перебор всех названий if available[i] then -- если в инвентаре такой есть for j = 1, math.ceil(available[i]/576) do -- разделить результат на стаки for c_slot = 1, 9 do -- цикл чистки зоны верстака if robot.count(workbench[c_slot]) > 0 then -- если слот не пуст for slot = 4, inventory do -- обойти весь инвентарь, кроме рабочей зоны if robot.count(slot) == 0 and (slot == 4 or slot == 8 or slot > 11) then -- если есть свободный robot.select(workbench[c_slot]) -- выбрать слот верстака robot.transferTo(slot) -- освободить слот break -- выйти из цикла end end if robot.count() > 0 then -- проверить на перегрузку robot.suck(1) -- забрать из буфера return true -- остановить упаковку end end end ------- основной цикл крафта ------- for slot = 4, inventory do -- цикл поиска фрагментов local item = controller.getStackInInternalSlot(slot) -- получить информацию о предмете if item and (slot == 4 or slot == 8 or slot > 11) then -- если есть предмет вне рабочей зоны if item.name:gsub('%g+:', '') == fragments[i] then -- сравнить по названию фрагмента robot.select(slot) -- при совпадении выбрать слот for n = 1, 9 do -- цикл заполнения рабочей зоны robot.transferTo(workbench[n], item.size/9) -- разделить текущий стак на 9 частей и перенести в верстак end if robot.count(1) == 64 then -- сброс при заполнении верстака break end end end end crafting.craft() -- создание блока -- цикл сортировки остатков for A = 1, inventory do -- основной проход local size = robot.count(A) -- получить количество предметов if size > 0 and size < 64 then -- если слот не пуст и не полон for B = A+1, inventory do -- проход сравнения if robot.compareTo(B) then -- если предметы одинаковые robot.select(A) -- выбрать слот robot.transferTo(B, 64-robot.count(B)) -- перенести до заполнения end if robot.count() == 0 then -- если слот освободился break -- прервать сравнение end end end end end end end end robot.suck(1) --- забрать предметы из буфера end
  45. 3 балла
    Может опечатка? Вот рабочий код: local robot = component.proxy(component.list('robot')()) while true do computer.beep(20, 0.5) if robot.detect(3) then robot.swing(3) end end
  46. 3 балла
    Как известно, жить у нас на сервере бывает опасно. Убийцы в плащах невидимках рыскают в поисках жертв, а табуны гриферов разоряют дома прямо на глазах у офигевающих владельцев. Моя программа не сможет предотвратить злодеяние, но запомнит негодяя и запишет его имя, дабы пострадавший знал, на кого направить свой гнев (кроме админа и модераторов, конечно). Суть Система защиты SpiderEye - это сеть motion sensor'ов в стратегически важных местах, которые соединены кабелями с компьютером. Помните, что сенсор - не радар. Он срабатывает только на существ в области прямой видимости. Сектор должен просматриваться и простреливаться, не создавайте укрытий гриферу! =) Сборка железа Конкретные параметры системы сильно зависят от того, сколько именно сенсоров вы присобачите к компьютеру. Процессор может обработать ограниченное количество компонентов. А именно: 1 уровень - 8 компонентов 2 уровень - 12 компонентов 3 уровень - 16 компонентов Сюда кроме сенсоров входит: монитор, клавиатура (?), красная плата/красный контроллер, видеокарта. Если вам потребуется больше компонентов - переносите программу на сервер (Server Rack и Server). Туда можно установить сразу несколько процессоров. Моя машина: Корпус 3 lvl. Процессор 3 lvl. Красная плата Видеокарта 2 lvl Две планки памяти 2 lvl Жесткий диск 1 lvl Монитор 2 lvl Клавиатура а также Датчики движения и Кабели Вы можете установить красную плату в компьютер, или подключить красный контроллер к сети. Тогда при сигнале тревоги компьютер (или контроллер) будет испускать сигнал редстоуна (примерно 10 секунд). Устанавливаем, подключаем, подаем питание и включаем. Софт Первым делом - установите на жесткий диск операционную систему (кто не знает как это сделать, тут есть инструкция: ссылка) Теперь программа. Ссылка на SpiderEye Terminal: pastebin.com/bTc7tJKv Сохраняем программу на жесткий под именем terminal. Настройка программы на владельца производится методом пилы и лома - открываем исходники и смотрим на первые строчки: -- Parameters -- owner = "Totoro" monsterlist = {"Spider", "Zombie", "Creeper", "Skeleton", "Enderman", "Sheep", "Cow", "Chicken"} howlerside = sides.back cooldown = 100 -- Colors -- backcolor = 0x000000 forecolor = 0xFFFFFF infocolor = 0x0066FF errorcolor = 0xFF0000 truecolor = 0x00FF00 -- **** -- Параметр owner - это ник владельца. Поменяйте на свой. Параметр monsterlist - это список животных, ради которых вы не хотите подрываться и бежать на улицу с мечом в руке. Я внес в него основную живность, которая шатается вокруг моего дома. В зависимости от области своего обитания, добавьте свои названия (гастов, например, или спрутов). Параметр howlerside - это сторона, на которую по тревоге компьютер начинает давать сигнал редстоуна. Если у вас плата - сторона дается относительно передней стороны компьютера. Если у вас блок-контроллер - сторона дается относительно сторон света. Если у вас нет ничего - забейте. Параметр cooldown - продолжительность тревоги в десятых секунды (примерно). По дефолту - десять секунд. Ниже - таблица цветов, которая хранит цветовую гамму программы. Вы можете поменять их на свои по желанию. Сохраняем и закрываем (Ctrl+S, затем Ctrl+W). Запускаем программу, набрав в консоли ее название. Использование Программа управляется с клавиатуры. Хоткеи: H - открывает/закрывает окно справки. В справке - кратко перечислены эти хоткеи. L - открывает/закрывает лог событий. В логе указаны дата, время и место засеченных движений. W - открывает/закрывает вайтлист. Вайтлист - список тех, на кого система не реагирует. По дефолту в этом списке только хозяин. A - добавляет новый сенсор или нового пользователя в вайтлист. Это зависит от того, какой именно список вы открыли. D - удаляет сенсор из таблицы или пользователя из вайтлиста. Тоже зависит от того, какой список вы смотрите. R - обновляет таблицу сенсоров. Если вы меняли сенсоры (они при этом сменяют адрес) или просто поудаляли половину сети слежения - обновите таблицу, нажав R. Программа автоматически удалит из таблицы все недействительные адреса. Также обновление происходит при перезапуске программы. М - включает/выключает обнаружение монстров. Когда включено - в левом-нижнем углу нарисована маленькая буква "M". Если вас достал паук, скребущий лапами по крыше, вы можете включить этот режим, и система начнет реагировать на монстров из списка тоже. По дефолту - отключено. Q - культурный выход из программы. В отличии от некультурного - например если питание вырубится, или вы нажмете Alt+Ctrl+C, все изменения в списках сохраняются, и вам не придется править их при следующем запуске. К логам это не относится. Они сохраняются всегда. Добавление сенсоров В начале экран терминала пуст. Все срабатывания сенсоров отмечены как Unknown и не записываются в логи. Вам необходимо добавить в таблицу все сенсоры, и подписать их. Нажмите "A". Сначала необходимо внести адрес сенсора. Его можно узнать с помощью анализатора - ПКМ на сенсоре. Впишите первые четыре буквы-цифры адреса. Например - "5bec" Затем программа потребует ввести навание. Обозначьте сенсор так, чтобы было понятно, где он. Например - "Secret Diamond Storage", или "Neighbor House". Итог У вас есть система, способная обнаружить живое существо (теоретически работает и на роботах) на подступах к вашей территории (и внутри нее). При обнаружении существа не внесенного в вайт лист или список монстров - подается сигнал редстоуна, на экран выводится мессейдж и делается запись в файл с логами на диске. Файлы логов расположены в той же папке, что и сама программа SpiderEye, в подкаталоге logs. Каждое событие хранится в файле помеченым датой, когда оно произошло. Например: "05_17_85.log". P.S. Это бета-версия программы. При обнаружении глюков, багов или шпионских закладок - сразу сообщайте мне! Спасибо за внимание.
  47. 3 балла
    😃 Надрал картинок блоков откудато (то ли в Blender отрендерил, то ли тупо с википедии содрал) и сложил из них основу. Потом немного допилил в Inkscape. Короче чудо тулзы не было, а просто немного свободного времени чтобы этой хренью заняться.
  48. 3 балла
    Так, я хз как реализовать это все, но давайте жрать кушать слона по кусочкам. Для начала надо вытащить из файла ник игрока. Не очень люблю такие страшные конструкции Больше предпочитаю построчно считать и все. Но надо так надо. Смотрим справку на функцию: string.sub (s, i [, j]) Возвращает подстроку s, которая начинается от i и продолжается до j; i и j могут быть отрицательными. Если j отсутствует, то он считается равным -1 (что то же самое, что и длина строки). В частности, вызов string.sub(s,1,j) возвратит начало строки s длиной j, а string.sub(s, -i) возвратит окончание строки s длиной i. Если, после преобразования отрицательных индексов, i становится меньше 1, он корректируется до 1. Если j больше длины строки, он корректируется до этой длины. Если, после этих исправлений, i стал больше j, функция возвратит пустую строку. string.find (s, pattern [, init [, plain]]) Разыскивает первое совпадение с шаблоном pattern в строке s. Если совпадение найдено, то find возвращает индексы строки s, где место этого совпадения началось и закончилось; в противном случае, она возвращает значение nil. Третий, необязательный аргумент init определяет место, откуда нужно начинать поиск; его значение по умолчанию равно 1 и может быть отрицательным. Значение true в качестве четвертого, дополнительного аргумента plain отключает возможности поиска совпадения с шаблоном, так что функция делает простую операцию "поиска подстроки", как если бы в pattern не было символов, считающихся магическими. Обратите внимание, что если задан аргумент plain, то аргумент init также должен быть задан. string.len (s) Принимает строку и возвращает её длину. Пустая строка "" имеет длину 0. Вложенные нули считаются, так что длина строки "a\000bc\000" равна 5. Это все что нам надо! Добавим немного магии, и получим следующий код local file = io.open('config.txt', 'r') --открыть файл конфигурации local a = file:read() --считать 1 строчку a = string.sub(a, string.find(a, '"')+1, string.len(a)-1) --порезать и выкинуть лишнее, обновить данные file:close() --закрыть конфигурацию На выходе в получаем только твой ник. Первая часть решена. Поехали дальше. Я это не понимаю но похоже это сделано для проверки ника того кто нажал на экран. Тут все в разы проще, отлавливаем данные нажатия в массив и потом проверяем их из массива с данными. b = {event.pull('touch')} if b[6] == a then print('Нажатие разрешено!') else print('Ты кто такой еще?') end Как-то так. Конечно вариант предложенный ECS мне больше по душе, но почему бы и не попробовать сделать как-то иначе?
  49. 3 балла
    Я решил попробовать написать софт для OpenComputers и написал небольшую софтину для получения информации о системе. Текущая версия: 2.0 Изменения в 2.0: Появились некоторая дополнительная информация о роботах В конце вместо противного писка звук 1000 длиной 0.1 секунду и 1500 длиной в 0.15 секунд Во всех значениях пропали числа после запятой Изменения в 1.01: ОЗУ теперь в КБ !Скриншоты версии: 1.0! Исходный код v 2.0: https://pastebin.com/4xEfjANx Скачать v. 2.0 (нужна интернет карта): pastebin get 4xEfjANx /bin/status.lua Исходный код v 1.01: https://pastebin.com/HtrMUj5p Скачать v 1.01 (нужна интернет карта): pastebin get HtrMUj5p /bin/status.lua Для запуска введите команду 'status' Скриншоты на ПК и роботе:
  50. 3 балла
    Если требуется получать время именно через интернет, то нет смысла поднимать собственный сервер, благо имеется тонна бесплатных API, мяу: -- Запрашиваем время у сервера в формате JSON local time = "" for chunk in require("internet").request("http://worldclockapi.com/api/json/utc/now") do time = time .. chunk end -- Выковыриваем timestamp в виде миллисекунд time = time:match("currentFileTime\":(%d+)") -- Обрезаем до секунд, т.к. 32-битная архитектура не поддерживает числа таких размеров time = tonumber(time:sub(1, -8)) -- Корректируем часовой пояс до требуемого. Москва - UTC+3. Один час - 3600 секунд time = time + 3 * 3600 -- Профит print("Времечко: " .. os.date("%H:%M:%S", time)) -- Можно и покомпактнее -- print("Времечко: " .. os.date("%H:%M:%S", tonumber(time:match("currentFileTime\":(%d+)"):sub(1, -8)) + 3 * 3600))
Эта таблица лидеров рассчитана в Москва/GMT+03:00
×
×
  • Создать...