Added ukranian lang, updated uCore, minor fixes

This commit is contained in:
Anuken
2018-03-19 20:46:40 -04:00
parent 58ed9754a4
commit 5323ef68ca
14 changed files with 738 additions and 152 deletions

View File

@ -25,7 +25,7 @@ allprojects {
appName = 'Mindustry' appName = 'Mindustry'
gdxVersion = '1.9.8' gdxVersion = '1.9.8'
aiVersion = '1.8.1' aiVersion = '1.8.1'
uCoreVersion = '39939f7' uCoreVersion = '9503bcb'
getVersionString = { getVersionString = {
String buildVersion = getBuildVersion() String buildVersion = getBuildVersion()

View File

@ -263,7 +263,7 @@ setting.sensitivity.name=Controller Sensitivity
setting.saveinterval.name=Autosave Interval setting.saveinterval.name=Autosave Interval
setting.seconds={0} Seconds setting.seconds={0} Seconds
setting.fullscreen.name=Fullscreen setting.fullscreen.name=Fullscreen
setting.multithread.name=Multithreading [scarlet](unstable!) setting.multithread.name=Multithreading
setting.fps.name=Show FPS setting.fps.name=Show FPS
setting.vsync.name=VSync setting.vsync.name=VSync
setting.lasers.name=Show Power Lasers setting.lasers.name=Show Power Lasers

View File

@ -0,0 +1,500 @@
text.about = Створено [ROYAL] Anuken. []\nСпочатку запис у [orange] GDL [] MM Jam.\nТворці:\n- SFX зроблено з [YELLOW] bfxr []\n- Музика зроблена [GREEN] RoccoW [] / Знайдено на [lime] FreeMusicArchive.org [] \nОсоблива подяка:\n- [coral] MitchellFJN []: екстенсивне тестування та відгуки\n- [sky] Luxray5474 []: робота з вікі, вклади коду\n- Всі бета-тестери на itch.io та Google Play\n
text.discord = Приєднуйтесь до нашого Discord!
text.changes = [SCARLET] Увага! \n[] Деякі важливі механіки гри були змінені.\n- [accent] Телепорти [] тепер використовують електроенергію. \n- [accent] Домінна піч [] та [accent] Тиглі [] тепер мають ліміт. \n- [accent] Тиглі [] зараз вимагають вугілля як паливо.
text.gameover = Ядро було зруйновано.
text.highscore = [YELLOW] Новий рекорд!
text.lasted = Ви тримались до хвилі
text.level.highscore = Рекорд: [accent] {0}
text.level.delete.title = Підтвердьте видалення
text.level.delete = Ви впевнені, що хочете видалити карту \"[orange] {0} \"?
text.level.select = Вибір рівня
text.level.mode = Ігровий режим
text.savegame = Зберегти гру
text.loadgame = Завантажити гру
text.joingame = Приєднатися\nдо гри
text.newgame=Нова гра
text.quit = Вийти
text.about.button = Про
text.name = Назва:
text.public = Публічний
text.players = {0} гравців онлайн
text.server.player.host = {0} (host)
text.players.single = {0} гравців онлайн
text.server.mismatch = Пакетна помилка: невідповідність версії версії клієнта / сервера. Переконайтеся, що ви та хост мають останню версію Mindustry!
text.server.closing = [accent] Закриття сервера ...
text.server.kicked.kick = Ви були вигнані з сервера!
text.server.kicked.invalidPassword = Невірний пароль!
text.server.kicked.clientOutdated = Застарілий клієнт! Оновіть свою гру!
text.server.kicked.serverOutdated = Застарілий сервер! Попросіть хост оновити!
text.server.connected = {0} приєднався.
text.server.disconnected = {0} від'єднано.
text.nohost = Неможливо розмістити сервер на власній карті!
text.hostserver = Хост-сервер
text.host = Хост
text.hosting = [accent] Відкриття сервера ...
text.hosts.refresh = Оновити
text.hosts.discovering = Знайомство з мережевими іграми
text.server.refreshing = Оновити сервери
text.hosts.none = [lightgray] Ніяких ігор у мережі не знайдено!
text.host.invalid = [scarlet] Неможливо підключитися до хосту.
text.server.friendlyfire = Дружній вогонь
text.server.add = Додати сервер
text.server.delete = Ви впевнені, що хочете видалити цей сервер?
text.server.hostname = Хост: {0}
text.server.edit = Редагувати сервер
text.joingame.byip = [] Приєднатися по IP ...[]
text.joingame.title = Приєднатися до гри
text.joingame.ip = IP
text.disconnect = Роз'єднано
text.connecting = [accent] Підключення ...
text.connecting.data = [accent] Завантаження світових даних ...
text.connectfail = [crimson] Не вдалося підключитися до сервера: [orange] {0}
text.server.port = Порт
text.server.addressinuse = Адреса вже використовується!
text.server.invalidport = Недійсний номер порту.
text.server.error = [crimson] Помилка хостингу сервера: [orange] {0}
text.tutorial.back = < Попер.
text.tutorial.next = Далі >
text.save.new = Нове збереження
text.save.overwrite = Ви впевнені, що хочете перезаписати цей слот для збереження?
text.overwrite = Перезаписати
text.save.none = Не знайдено жодних збережень!
text.saveload = [accent] Збереження ...
text.savefail = Не вдалося зберегти гру!
text.save.delete.confirm = Ви впевнені, що хочете видалити це збереження?
text.save.delete = Видалити
text.save.export = Експорт збереження
text.save.import.invalid = [orange] Це збереження недійсне!
text.save.import.fail = [crimson] Не вдалося імпортувати збереження: [orange] {0}
text.save.export.fail = [crimson] Не вдалося експортувати збереження: [orange] {0}
text.save.import = Імпортувати збереження
text.save.newslot = Назва збереження:
text.save.rename = Переіменувати
text.save.rename.text = Нова назва:
text.selectslot = Виберіть збереження.
text.slot = [accent] слот {0}
text.save.corrupted = [orange] Збережений файл пошкоджений або він невірний!
text.empty = <порожньо>
text.on = Увімкнути
text.off = Вимкнути
text.save.autosave = Автозбереження: {0}
text.save.map = Карта
text.save.wave = Хвиля {0}
text.save.difficulty = Складність
text.save.date = Останнє збережено: {0}
text.confirm = Підтвердити
text.delete = Видалити
text.ok = ОК
text.open = Відкрити
text.cancel = Скасувати
text.openlink = Відкрити посилання
text.back = Назад
text.quit.confirm = Ти впевнений що хочеш піти?
text.loading = [accent] Завантаження ...
text.wave = [orange] хвиля {0}
text.wave.waiting = Хвиля через {0}
text.waiting = Очікування…
text.enemies = {0} Вороги
text.enemies.single = Противник
text.loadimage = Завантажити зображення
text.saveimage = Зберегти зображення
text.oregen = Генерація руд
text.editor.badsize = [orange] Недійсні розміри зображення! [] Дійсні розміри карти: {0}
text.editor.errorimageload = Помилка завантаження файлу зображень: [orange] {0}
text.editor.errorimagesave = Помилка збереження файлу зображення: [orange] {0}
text.editor.generate = Генератор
text.editor.resize = Змінити розмір
text.editor.loadmap = // Завантажити карту
text.editor.savemap = Зберегти карту
text.editor.loadimage = Завантажити зображення
text.editor.saveimage = Зберегти зображення
text.editor.unsaved = [scarlet] У вас є незбережені зміни! [] Ви впевнені, що хочете вийти?
text.editor.brushsize = Розмір пензля: {0}
text.editor.noplayerspawn = Ця карта не має ігрового поля для гравця!
text.editor.manyplayerspawns = Карти не можуть мати більше одного ігрового поля для гравців!
text.editor.manyenemyspawns = Не може бути більше ніж {0} ворожих точок!
text.editor.resizemap = Змінити розмір карти
text.editor.resizebig = [scarlet] Попередження! [] Карти, розмір яких перевищує 256 одиниць, можуть виснути і можуть бути нестабільними.
text.editor.mapname = Назва карти:
text.editor.overwrite = [accent] Попередження! Це перезаписує існуючу карту.
text.editor.failoverwrite = [crimson] Неможливо перезаписати карту за замовчуванням!
text.editor.selectmap = Виберіть карту для завантаження:
text.width = Ширина
text.height = Висота
text.randomize = Рандомізувати
text.apply = Застосувати
text.update = Оновити
text.menu = Меню
text.play = Відтворити
text.load = Завантаження
text.save = Зберегти
text.language.restart = Будь ласка, перезапустіть свою гру, щоб налаштування мови набули чинності.
text.settings.language = Мова
text.settings = Налаштування
text.tutorial = Навчальний\nпосібник
text.editor = Редактор
text.mapeditor = Редактор карт
text.donate = Підтримати проект
text.settings.reset = Скинути до стандартних
text.settings.controls = Елементи управління
text.settings.game = Гра
text.settings.sound = Звук
text.settings.graphics = Графіка
text.upgrades = Оновлення
text.purchased = [LIME] Створено!
text.weapons = Зброя
text.paused = Пауза
text.respawn = Відновлення за
text.info.title = [accent] інформація
text.error.title = [crimson] Виникла помилка
text.error.crashmessage = [SCARLET] Виникла несподівана помилка, що призвела до збою. [] Будь ласка, повідомте про конкретні обставини, розробнику: [ORANGE] anukendev@gmail.com []
text.error.crashtitle = Виникла помилка
text.mode.break = Режим зносу: {0}
text.mode.place = Режим будівництва: {0}
placemode.hold.name = Лінія
placemode.areadelete.name = Площа
placemode.touchdelete.name = Дотик
placemode.holddelete.name = Утримування.
placemode.none.name = (None)
placemode.touch.name = Дотик
placemode.cursor.name = курсор
text.blocks.extrainfo = [accent] додатковий інформаційний блок:
text.blocks.blockinfo = Блокування інформації
text.blocks.powercapacity = Потужність
text.blocks.powershot = Потужність / постріл
text.blocks.powersecond = Потужність / секунда
text.blocks.powerdraindamage = Потужність дренажу / пошкодження
text.blocks.shieldradius = Радіус щита
text.blocks.itemspeedsecond = Швидкість / секунда
text.blocks.range = Радіус
text.blocks.size = Розмір
text.blocks.powerliquid = Потужність / Рідина
text.blocks.maxliquidsecond = Макс. Рідина / секунда
text.blocks.liquidcapacity = Ємкість рідини
text.blocks.liquidsecond = Рідина / секунда
text.blocks.damageshot = Пошкодження / постріл
text.blocks.ammocapacity = Місткість боєприпасів
text.blocks.ammo = Набої
text.blocks.ammoitem = Боєприпаси / предмет
text.blocks.maxitemssecond = Макс. Елементи / секунду
text.blocks.powerrange = Радіус потужності
text.blocks.lasertilerange = Радіус лазерних плиток
text.blocks.capacity = Ємкість
text.blocks.itemcapacity = Ємкість предмету
text.blocks.maxpowergenerationsecond = Максимальна потужність / секунда
text.blocks.powergenerationsecond = Потужність / секунда
text.blocks.generationsecondsitem = Генерація за секунду / предмет
text.blocks.input = Ввід
text.blocks.inputliquid = Ввід речовини
text.blocks.inputitem = Вхідний матеріал
text.blocks.output = Вивід
text.blocks.secondsitem = Секунда / предмет
text.blocks.maxpowertransfersecond = Максимальна передача потужності / секунда
text.blocks.explosive = Вибухонебезпечний!
text.blocks.repairssecond = Ремонт / секунда
text.blocks.health = Здоров'я
text.blocks.inaccuracy = Неточність
text.blocks.shots = Постріли
text.blocks.shotssecond = Постріли / секунду
text.blocks.fuel = Паливо:
text.blocks.fuelduration = Тривалість палива
text.blocks.maxoutputsecond = Макс. Вихід / секунду
text.blocks.inputcapacity = Вхідна ємність
text.blocks.outputcapacity = Випускна ємність
text.blocks.poweritem = Потужність / виріб
text.placemode = Місцевий режим
text.breakmode = Перерваний режим
text.health = Здоров'я
setting.difficulty.easy = Легкий
setting.difficulty.normal = Нормальний
setting.difficulty.hard = Важкий
setting.difficulty.insane = Божевільний
setting.difficulty.purge = Очистити
setting.difficulty.name = Складність
setting.screenshake.name = Тряска екрана
setting.smoothcam.name = Гладка камера
setting.indicators.name = Індикатори ворога
setting.effects.name = Ефекти відображення
setting.sensitivity.name = Чутливість контролера
setting.saveinterval.name = Інтервал автозбереження
setting.seconds = {0} секунд
setting.fullscreen.name = Повноекранний
setting.multithread.name = Багатопотоковий [scarlet] (нестабільний!)
setting.fps.name = Показати FPS
setting.vsync.name = VSunc
setting.lasers.name = Показати енергетичні лазери
setting.healthbars.name = Показати здоров'я
setting.pixelate.name = Пікселяція екрану
setting.musicvol.name = Гучність музики
setting.mutemusic.name = Вимкнути музику
setting.sfxvol.name = Гучність ефектів
setting.mutesound.name = Вимкнути звук
map.maze.name = Лабіринт
map.fortress.name = Фортеця
map.sinkhole.name = Свердловина
map.caves.name = Печери
map.volcano.name = Вулкан
map.caldera.name = Кальдера
map.scorch.name = Мертва земля
map.desert.name = Пустеля
map.island.name = Острів
map.grassland.name = Пасовища
map.tundra.name = Тундра
map.spiral.name = Спіраль
map.tutorial.name = Навчання
tutorial.intro.text = [yellow] Ласкаво просимо до підручника. [] Для початку натисніть \"далі\".
tutorial.moveDesktop.text = Для переміщення використовуйте клавіші [orange] [[WASD] []. Утримуйте [orange] SHIFT[], для прискорення. Утримуйте [orange] CTRL [], використовуючи [orange] колесо прокручування [] для збільшення або зменшення.
tutorial.shoot.text = Використовуйте мишу, щоб націлитись, утримуйте [orange] ліву кнопку миші [], щоб стріляти. Попрактикуйтесь на [yellow] мішені [].
tutorial.moveAndroid.text = Щоб перетягнути панораму, перетягніть один палець по екрану. Використовуйте два пальця, щоб збільшити чи зменшити маштаб.
tutorial.placeSelect.text = Спробуйте вибрати [yellow] конвеєр [] у меню блоку внизу справа.
tutorial.placeConveyorDesktop.text = Використовуйте [orange] [[колесико миші] [], щоб повернути конвеєр [orange] вперед [], а потім помістіть його в [yellow] позначене місце [], використовуючи [orange] [[ліву кнопку миші] [].
tutorial.placeConveyorAndroid.text = Використовуйте [orange] [[кнопку оберту] [], щоб обернути конвеєр [оранжевий] вперед [], перетягуйте його одним пальцем, а потім помістіть його в [yellow] позначене місце [], використовуючи [orange] [[галочка][].
tutorial.placeConveyorAndroidInfo.text = Крім того, ви можете натиснути піктограму перехрестя внизу ліворуч, щоб переключитися на [orange] [[сенсорний режим]] [], і помістити блоки, натиснувши на екран. У сенсорному режимі блоки можна повертати зі стрілкою внизу ліворуч. Натисніть [yellow] наступний [], щоб спробувати.
tutorial.placeDrill.text = Тепер виберіть та розмістіть [yellow] кам'яне свердло [] у зазначеному місці.
tutorial.blockInfo.text = Якщо ви хочете дізнатись більше про блок, ви можете торкнутися [orange] знак питання [] у верхньому правому куті, щоб прочитати його опис.
tutorial.deselectDesktop.text = Ви можете вимкнути блок, використовуючи [orange] [[клацання правою кнопкою миші] [].
tutorial.deselectAndroid.text = Ви можете скасувати вибір блоку, натиснувши кнопку [orange] X [].
tutorial.drillPlaced.text = Дриль тепер видобуває [yellow] камінь, [] та виведе його на конвеєр, а потім переміщає його в [yellow] ядро [].
tutorial.drillInfo.text = Різні руди потребують різних дрилі. Камінь вимагає кам'яні свердла, залізо вимагає залізні свердла та ін
tutorial.drillPlaced2.text = Переміщення елементів у ядро ​​вказує їх у ваш [yellow] предметний інвентар [] у верхньому лівому куті. Розміщення блоків використовує предмети з вашого інвентарю.
tutorial.moreDrills.text = Ви можете пов'язати багато свердлів і конвеєрів разом в одну гілку конвеєра.
tutorial.deleteBlock.text = Ви можете видалити блоки, натиснувши правою клавішею [orange] правою кнопкою миші [] по блоці, який ви хочете видалити. Спробуйте видалити цей конвеєр.
tutorial.deleteBlockAndroid.text = Ви можете видалити блоки за допомогою [orange], перехрестя [] в меню [mode] зламу [orange] у нижньому лівому куті та натиснувши на блок. Спробуйте видалити цей конвеєр.
tutorial.placeTurret.text = Тепер виділіть та розмістіть [yellow] турель [] у [yellow] позначеному місці [].
tutorial.placedTurretAmmo.text = Ця турель тепер приймає [yellow] боєприпас [] з конвеєра. Ви можете побачити, скільки боєприпасів вона має, натискаючи на неї і перевіряючи [green] зелену полоску [].
tutorial.turretExplanation.text = Турелі будуть автоматично стріляти у найближчого ворога, якщо вони мають достатню кількість боєприпасів.
tutorial.waves.text = Кожні [yellow] 60 [] секунд, хвиля [coral] ворогів [] буде виникати в певних місцях і намагатися знищити ядро.
tutorial.coreDestruction.text = Ваша мета полягає в тому, щоб [yellow] захищати ядро []. Якщо ядро ​​знищено, ви [coral] програєте[].
tutorial.pausingDesktop.text = Якщо вам коли-небудь потрібно зробити перерву, натисніть кнопку [orange] паузи [] у верхньому лівому куті або на кнопку [orange] пропуск [], щоб призупинити гру. Ви можете вибрати і розмістити блоки під час призупинення, але не можете переміщатися чи стріляти.
tutorial.pausingAndroid.text = Якщо вам коли-небудь потрібно зробити перерву, натисніть кнопку [orange] пауза [] у верхньому лівому куті, щоб призупинити гру. Ти можеш ще знищувати та будувати блоки під час призупинення.
tutorial.purchaseWeapons.text = Ви можете придбати нову [yellow] зброю [] для вашого механізму, відкривши меню оновлення в лівому нижньому кутку.
tutorial.switchWeapons.text = Перемикати зброю будь-яким натисканням його піктограми внизу ліворуч або за допомогою цифр [orange] [[1-9] [].
tutorial.spawnWave.text = Ось хвиля зараз. Знищи їх
tutorial.pumpDesc.text = У пізніших хвилях, можливо, доведеться використовувати [yellow] насоси [] для розподілу рідин для генераторів або екстракторів.
tutorial.pumpPlace.text = Насоси працюють аналогічно свердлам, за винятком того, що вони виробляють рідини замість предметів. Спробуйте встановити насос на [yellow] призначене мастило [].
tutorial.conduitUse.text = Тепер покладіть [orange] трубопровід [], віддаляючись від насоса.
tutorial.conduitUse2.text = І ще кілька ...
tutorial.conduitUse3.text = І ще кілька ...
tutorial.generator.text = Тепер, помістіть блок [orange] ​​базовий генератор енергії [] в кінці каналу.
tutorial.generatorExplain.text = Цей генератор тепер створить [yellow] енергію [] від масла.
tutorial.lasers.text = Потужність розподіляється за допомогою [yellow] лазерів потужності []. Поверніть і помістіть його тут.
tutorial.laserExplain.text = Тепер генератор переведе енергію в лазерний блок. Промінь [yellow] непрозорий [] означає, що в даний час він передає потужність, а промінь [yellow] прозорий [] означає, що це не так.
tutorial.laserMore.text = Ви можете перевірити, скільки енергії в блоку, наведіть курсор миші на нього і перевірте [yellow] жовту стрічку [] у верхній частині екрана.
tutorial.healingTurret.text = Цей лазер може бути використаний для живлення турелі для ремонту [lime] []. Помістіть одну тут.
tutorial.healingTurretExplain.text = Поки вона має енергію, ця турель може [lime] відремонтувати блоки. [] Під час гри постарайтеся збудувати одну таку чим швидше!
tutorial.smeltery.text = Для багатьох блоків потрібна [orange] сталь [], для цього потрібна[orange] доминна піч [] . Місце тут.
tutorial.smelterySetup.text = Ця піч буде тепер виробляти [orange] сталь [] із вхідного заліза, використовуючи вугілля як паливо.
tutorial.tunnelExplain.text = Також зауважте, що елементи проходять через [yellow] тунельний блок [] і з'являються з іншого боку, проходячи через кам'яний блок. Майте на увазі, що тунелі можуть проходити лише до 2 блоків.
tutorial.end.text = Ви завершили підручник! Удачі!
text.keybind.title = Ключ перемотки
keybind.move_x.name = move_x
keybind.move_y.name = move_y
keybind.select.name = Вибрати
keybind.break.name = {0}break{/0}{1}; {/1}
keybind.shoot.name = Постріл
keybind.zoom_hold.name = zoom_hold
keybind.zoom.name = Збільшити
keybind.block_info.name = Інформація про блок
keybind.menu.name = Меню
keybind.pause.name = Пауза
keybind.dash.name = Тире
keybind.chat.name = Чат
keybind.player_list.name = Список гравців
keybind.console.name = // Консоль 1
keybind.rotate_alt.name = rotate_alt
keybind.rotate.name = Повернути
keybind.weapon_1.name = Зброя!
keybind.weapon_2.name = Зброя!
keybind.weapon_3.name = Зброя!
keybind.weapon_4.name = Зброя!
keybind.weapon_5.name = Зброя!
keybind.weapon_6.name = Зброя!
mode.waves.name = Хвилі
mode.sandbox.name = Пісочниця
mode.freebuild.name = Вільний режим
upgrade.standard.name = Стандартний
upgrade.standard.description = Стандартний механ.
upgrade.blaster.name = Бластер
upgrade.blaster.description = Стріляє повільно, слабкі кулі.
upgrade.triblaster.name = Трипластер
upgrade.triblaster.description = Вистрілює 3 кулі в розповсюдженні.
upgrade.clustergun.name = Касетна гармата
upgrade.clustergun.description = Вистрілює неточними вибуховими гранатами.
upgrade.beam.name = Пушечна гармата
upgrade.beam.description = Вистрілює далекобійним,пробірний лазерний промінь.
upgrade.vulcan.name = Вулкан
upgrade.vulcan.description = Вистрілює шквал швидких куль.
upgrade.shockgun.name = Шок-пушка
upgrade.shockgun.description = Стріляє руйнівним вибухом заряженої шрапнелі.
item.stone.name = Камінь
item.iron.name = Залізо
item.coal.name = Вугівалля
item.steel.name = Сталь
item.titanium.name = Титан
item.dirium.name = Дириум
item.uranium.name = Уран
item.sand.name = Пісок
liquid.water.name = Вода
liquid.plasma.name = Плазма
liquid.lava.name = Лава
liquid.oil.name = Нафта
block.weaponfactory.name = Фабрика зброї
block.weaponfactory.fulldescription = Використовується для створення зброї для гравця mech. Натисніть, щоб використати. Автоматично приймає ресурси з основного ядра.
block.air.name = Повітря
block.blockpart.name = Блокчастина
block.deepwater.name = Глибока вода
block.water.name = Вода
block.lava.name = Лава
block.oil.name = Нафта
block.stone.name = Камінь
block.blackstone.name = Чорний камінь
block.iron.name = Залізо
block.coal.name = Вугілля
block.titanium.name = Титан
block.uranium.name = Уран
block.dirt.name = Бруд
block.sand.name = Пісок
block.ice.name = Лід
block.snow.name = Сніг
block.grass.name = Трава
block.sandblock.name = Блок піску
block.snowblock.name = Блок снігу
block.stoneblock.name = Блок камню
block.blackstoneblock.name = Блок чорного камню
block.grassblock.name = Блок бруду
block.mossblock.name = Моссблок
block.shrub.name = Чагарник
block.rock.name = Камень
block.icerock.name = Ледяний камень
block.blackrock.name = Чорний камінь
block.dirtblock.name = Блок землі
block.stonewall.name = Кам'яна стіна
block.stonewall.fulldescription = Недорогий захисний блок. Корисно для захисту ядра та турелі в перші кілька хвиль.
block.ironwall.name = Залізна стіна
block.ironwall.fulldescription = Основний захисний блок. Забезпечує захист від ворогів.
block.steelwall.name = Сталева стіна
block.steelwall.fulldescription = Стандартний захисний блок. адекватний захист від ворогів.
block.titaniumwall.name = Титанова стіна
block.titaniumwall.fulldescription = Сильний захисний блок. Забезпечує захист від ворогів.
block.duriumwall.name = Діріумова стіна
block.duriumwall.fulldescription = Дуже сильний захисний блок. Забезпечує захист від ворогів.
block.compositewall.name = Композитна стіна
block.steelwall-large.name = Велика сталева стіна
block.steelwall-large.fulldescription = Стандартний захисний блок. Поєднує в собі кілька блоків.
block.titaniumwall-large.name = Велика титанова стіна
block.titaniumwall-large.fulldescription = Сильний захисний блок. Поєднує в собі кілька блоків.
block.duriumwall-large.name = Велика дирмітова стіна
block.duriumwall-large.fulldescription = Дуже сильний захисний блок.Поєднує в собі кілька блоків.
block.titaniumshieldwall.name = Стіна з щитом
block.titaniumshieldwall.fulldescription = Сильний захисний блок з додатковим вбудованим щитом. Потрібна енергія. Використовує енергію для поглинання ворожих куль. Рекомендується використовувати силові пристосування для забезпечення енергії цього блоку.
block.repairturret.name = Ремонтна турель
block.repairturret.fulldescription = Ремонтує недалекі пошкодженні блоки.Повільний темп. Використовує невелику кількість енергії.
block.megarepairturret.name = Ремонтна турель II
block.megarepairturret.fulldescription = Ремонтує недалекі пошкодженні блоки.Збільшений радіус та швидший темп ремонту . Використовує багато енергії.
block.shieldgenerator.name = Генератор щиту
block.shieldgenerator.fulldescription = Передовий захисний блок. Захищає всі блоки в радіусі від нападу. Не вкористовує енергію при бездіяльності, але швидко витрачає енергію на захист від куль.
block.door.name = Двері
block.door.fulldescription = Блок, який можна відкрити та закрити, торкнувшись його.
block.door-large.name = Великі двері
block.door-large.fulldescription = Блок, який можна відкрити та закрити, торкнувшись його.
block.conduit.name = Трубопровід
block.conduit.fulldescription = Основний транспортний блок. Працює як конвеєр, але з рідинами. Найкраще використовується з насосами або іншими трубопроводами. Може використовуватися як міст через рідини для ворогів та гравців.
block.pulseconduit.name = Імпульсний канал
block.pulseconduit.fulldescription = Покращенний блок перевезення рідин. Транспортує рідини швидше і зберігає більше стандартних каналів.
block.liquidrouter.name = маршрутизатор для рідини
block.liquidrouter.fulldescription = Працює аналогічно маршрутизатору. Приймає рідину ввід з одного боку і виводить його на інші сторони. Корисний для розщеплення рідини з одного каналу на кілька інших трубопроводів.
block.conveyor.name = Конвеєр
block.conveyor.fulldescription = Базовий транспортний блок. Переміщує предмети вперед і автоматично вкладає їх у турелі або ремісники. Поворотний Може використовуватися як міст через рідину для ворогів та гравців.
block.steelconveyor.name = Сталевий конвеєр
block.steelconveyor.fulldescription = Розширений блок транспортування предметів. Переміщення елементів швидше, ніж стандартні конвеєри.
block.poweredconveyor.name = Імпульсний конвеєр
block.poweredconveyor.fulldescription = Кінцевий транспортний блок. Переміщення елементів швидше, ніж сталеві конвеєри.
block.router.name = Маршрутизатор
block.router.fulldescription = Приймає елементи з одного напрямку і виводить їх на 3 інших напрямках. Можна також зберігати певну кількість предметів. Використовується для розщеплення матеріалів з одного свердла на декілька башточок.
block.junction.name = Міст
block.junction.fulldescription = Виступає як міст для двох перехресних конвеєрних стрічок. Корисне у ситуаціях з двома різними конвеєрами, що несуть різні матеріали в різних місцях.
block.conveyortunnel.name = Конвеєрний тунель
block.conveyortunnel.fulldescription = Транспортує предмети під блоками. Щоб використати, помістіть один тунель, що веде у блок, щоб бути підсвіченим, а один - з іншого боку. Переконайтеся, що обидва тунелі стикаються з протилежними напрямками, тобто до блоків, які вони вводять або виводять.
block.liquidjunction.name = Міст для рідини
block.liquidjunction.fulldescription = Діє як міст для двох перехресних трубопроводів. Корисно в ситуаціях з двома різними трубами, що несуть різні рідини в різних місцях.
block.liquiditemjunction.name = Перехрестя рідкого пункту
block.liquiditemjunction.fulldescription = Виступає як міст для перетину трубопроводів і конвеєрів.
block.powerbooster.name = Підсилювач потужності
block.powerbooster.fulldescription = Поширює енергію на всі блоки в межах його радіуса.
block.powerlaser.name = Енергетичний лазер
block.powerlaser.fulldescription = Створює лазер, який передає енергію блоку перед ним. Не створює жодної сили сама. Найкраще використовується з генераторами або іншими лазерами.
block.powerlaserrouter.name = Лазерний маршрутизатор
block.powerlaserrouter.fulldescription = Лазер, який розподіляє енергію у три напрямки одночасно. Корисно в ситуаціях, коли потрібно живити кілька блоків від одного генератора.
block.powerlasercorner.name = Лазерний кут
block.powerlasercorner.fulldescription = Лазер, який розподіляє енергію одночасно на два напрямки. Корисно в ситуаціях, коли потрібно живити кілька блоків від одного генератора, а маршрутизатор неточний.
block.teleporter.name = Телепорт
block.teleporter.fulldescription = Продвинутий блок транспортування предметів.Щоб телепортувати предмети з одного місця в інше потрібно збудувати 2 телепорти і назначити на них одинаковий колір. Використовує енергію. Натисніть, щоб змінити колір.
block.sorter.name = Сортувальник
block.sorter.fulldescription = Сортує предмети за типом матеріалу. Матеріал для прийняття позначається кольором у блоці. Всі елементи, що відповідають матеріалу сортування, виводяться вперед, а все інше виводить ліворуч і праворуч.
block.core.name = Ядро
block.pump.name = Насос
block.pump.fulldescription = Насоси рідини з вихідного блоку - зазвичай вода, лава чи олія. Виводить рідину в сусідні трубопроводи.
block.fluxpump.name = Флюсовий насос
block.fluxpump.fulldescription = Розширений варіант насоса. Зберігає більше рідини та перекачує швидше.
block.smelter.name = Плавильня
block.smelter.fulldescription = Основний ремісничий блок. Коли вводиться 1 залізо та 1 вугілля в якості палива, виводить одну сталь. Рекомендується вводити залізо та вугілля на різних поясах, щоб запобігти засміченню.
block.crucible.name = Тигель
block.crucible.fulldescription = Розширений блок обробки. При введенні 1 титану, 1 сталі та 1 вугілля в якості пального, виводить один дирний. Рекомендується вводити вугілля, сталь та титан на різних поясах, щоб запобігти засміченню.
block.coalpurifier.name = вугільний екстрактор
block.coalpurifier.fulldescription = Основний екстрактор. Виходить вугілля при постачанні великої кількості води та каменю.
block.titaniumpurifier.name = Титановий екстрактор
block.titaniumpurifier.fulldescription = Стандартний блок екстрактора. Виходить титан при постачанні великої кількості води та заліза.
block.oilrefinery.name = Нафтопереробний завод
block.oilrefinery.fulldescription = Очищує велику кількість нафти і перетворює на вугілля. Корисний для заправки вугільних башточок, коли вугільні родовища є дефіцитними.
block.stoneformer.name = Кам'янний екстрактор
block.stoneformer.fulldescription = Здавлюється рідка лава в камінь. Корисно для виготовлення великої кількості каменю для очищувачів вугілля.
block.lavasmelter.name = Лавовий завод
block.lavasmelter.fulldescription = Використовує лаву для перетворення залізо на сталь. Альтернатива плавильні. Корисно в ситуаціях, коли вугілля є дефіцитним.
block.stonedrill.name = Кам'янна свердловина
block.stonedrill.fulldescription = Основна свердловина.Розміщюється на кам'яній плитці виводить камінь повільними темпами.
block.irondrill.name = Залізна свердловина
block.irondrill.fulldescription = Базова свердловина.Розміщюється на родовищі залізної руди, випускає залізо в повільному темпі.
block.coaldrill.name = Вугільна свердловина
block.coaldrill.fulldescription = Базова свердловина.Розміщюється на родовищі вугільної руди ,видобуває вугілля повільними темпами.
block.uraniumdrill.name = Уранова свердловина
block.uraniumdrill.fulldescription = Продвинута свердловина. Розміщюється на родовищі уранової руди.Видобуток урану відбувається повільними темпами.
block.titaniumdrill.name = Титанова свердловина
block.titaniumdrill.fulldescription = Продвинута свердловина.Розміщюється на родовищі титанової руди, Видобуток титану відбувається повільним темпом.
block.omnidrill.name = Убер свердловина
block.omnidrill.fulldescription = Кінцева свердловина.Дуже швидко видобуває будь-який вид руди.
block.coalgenerator.name = Вугільний генератор
block.coalgenerator.fulldescription = Основний генератор. Генерує енергію з вугілля. Виводиться потужність лазерів на 4 сторони.
block.thermalgenerator.name = Теплогенератор
block.thermalgenerator.fulldescription = Генерує енергію від лави. Виводиться потужність лазерів на 4 сторони.
block.combustiongenerator.name = Генератор горіння
block.combustiongenerator.fulldescription = Генерує енергію з нафти. Виводиться потужність лазерів на 4 сторони.
block.rtgenerator.name = RTG генератор
block.rtgenerator.fulldescription = Генерує невелику кількість енергії з радіоактивного розпаду урану. Виводиться потужність лазерів на 4 сторони.
block.nuclearreactor.name = Ядерний реактор
block.nuclearreactor.fulldescription = Розширений варіант RTG Generator і кінцевий генератор електроенергії. Генерує енергію з урану. Потребує постійного водяного охолодження.Сильно вибухне якщо не буде постачання води у великії кількості
block.turret.name = Турель
block.turret.fulldescription = Базова, дешева турель. Використовує камінь для боєприпасів. Має трохи більше діапазону, ніж подвійна турель.
block.doubleturret.name = Подвійна турель
block.doubleturret.fulldescription = Дещо потужна версія турель. Використовує камінь для боєприпасів. Значно більший урон, але менший діапазон. Вистрілює дві кулі.
block.machineturret.name = Кулеметна турель
block.machineturret.fulldescription = Стандартна всеосяжна турель. Використовує залізо для боєприпасів. Має швидку швидкість пострілу і гідну шкоду.
block.shotgunturret.name = Розріджуюча турель
block.shotgunturret.fulldescription = Стандартна турель. Використовує залізо для боєприпасів. Вистрілює 7 куль навколо себе.Наносить значних ушкоджень,звісно якщо поцілить :)
block.flameturret.name = Вогнемет
block.flameturret.fulldescription = Продвинута турель ближнього діапазону. Використовує вугілля для боєприпасів. Має дуже низький радіус, але дуже високий збиток. Добре для близьких дистанцій. Рекомендується використовувати за стінами.
block.sniperturret.name = Лазерна турель.
block.sniperturret.fulldescription = Продвинута далекобійна турель. Використовує сталь для боєприпасів. Дуже високий збиток, але низький рівень урону. Дорогі для використання, але можуть бути розташовані далеко від ліній ворога через його радіус.
block.mortarturret.name = Флак турель
block.mortarturret.fulldescription = Продвинута,неточна турель. Використовує вугілля для боєприпасів. Стріляє кулями, що вибухають у шрапнеллв. Корисне для великих натовпів ворогів.
block.laserturret.name = Лазерна турель
block.laserturret.fulldescription = Продвинута однопушечна турель. Використовує енергію. Хороша на середніх дистанціях. Ніколи не пропускає.
block.waveturret.name = Тесла
block.waveturret.fulldescription = Передова багатоцільова турель. Використовує енергію. Середній радіус. Ніколи не пропускає. Активно знижує, але може вражати декількох ворогів одночасно з ланцюговим освітленням.
block.plasmaturret.name = Плазмова турель
block.plasmaturret.fulldescription = Дуже продвинута версія Вогнеметної турелі. Використовує вугілля як боєприпаси. Дуже високий урон, від близької до середньої дистанції.
block.chainturret.name = Уранова турель
block.chainturret.fulldescription = Остаточна швидкістна вежа. Використовує уран як боєприпаси. Вистрілює великі кулі при високій швидкості вогню. Середній радіус. Промінь кілька плиток. Надзвичайно жорсткий.
block.titancannon.name = Титанова гармата
block.titancannon.fulldescription = Найбільш далекобійна турель. Використовує уран як боєприпаси. Вистрілює великі снаряди. Далекобійний. Промінь кілька плиток. Надзвичайно жорсткий.
block.playerspawn.name = Спавн Гравця
block.enemyspawn.name = Спавн ворогів

View File

@ -70,10 +70,13 @@ char id=98 x=204 y=102 width=22 height=22 xoffset=-1 yoffset=15 x
char id=99 x=226 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=99 x=226 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=100 x=248 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=100 x=248 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=101 x=270 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=101 x=270 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=1108 x=270 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=102 x=292 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=102 x=292 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=103 x=314 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=103 x=314 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=104 x=336 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=104 x=336 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=105 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 char id=105 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0
char id=1110 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0
char id=1111 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0
char id=106 x=372 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=106 x=372 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=107 x=394 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=107 x=394 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0
char id=108 x=416 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 char id=108 x=416 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0

View File

@ -1,7 +1,7 @@
#Autogenerated file. Do not modify. #Autogenerated file. Do not modify.
#Fri Mar 16 22:39:48 EDT 2018 #Mon Mar 19 20:46:00 EDT 2018
version=release version=release
androidBuildCode=446 androidBuildCode=449
name=Mindustry name=Mindustry
code=3.4 code=3.4
build=custom build build=custom build

View File

@ -92,7 +92,7 @@ public class Vars{
public static final int tilesize = 8; public static final int tilesize = 8;
public static final Locale[] locales = {new Locale("en"), new Locale("fr", "FR"), new Locale("ru"), new Locale("pl", "PL"), public static final Locale[] locales = {new Locale("en"), new Locale("fr", "FR"), new Locale("ru"), new Locale("uk", "UA"), new Locale("pl", "PL"),
new Locale("de"), new Locale("es", "LA"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID")}; new Locale("de"), new Locale("es", "LA"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID")};
public static final Color[] playerColors = { public static final Color[] playerColors = {

View File

@ -122,6 +122,8 @@ public class Control extends Module{
"lastBuild", 0 "lastBuild", 0
); );
Log.info("{0}", (int)'ї');
KeyBinds.load(); KeyBinds.load();
for(Map map : world.maps().list()){ for(Map map : world.maps().list()){

View File

@ -12,6 +12,7 @@ import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Net.SendMode;
import io.anuke.mindustry.net.NetworkIO; import io.anuke.mindustry.net.NetworkIO;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.net.TraceInfo;
import io.anuke.mindustry.resource.*; import io.anuke.mindustry.resource.*;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.Placement;
@ -65,8 +66,7 @@ public class NetServer extends Module{
String ip = Net.getConnection(id).address; String ip = Net.getConnection(id).address;
admins.setKnownName(ip, packet.name); admins.updatePlayerJoined(uuid, ip, packet.name);
admins.setKnownIP(uuid, ip);
admins.getTrace(ip).uuid = uuid; admins.getTrace(ip).uuid = uuid;
admins.getTrace(ip).android = packet.android; admins.getTrace(ip).android = packet.android;
@ -150,6 +150,7 @@ public class NetServer extends Module{
Net.send(dc, SendMode.tcp); Net.send(dc, SendMode.tcp);
Platform.instance.updateRPC(); Platform.instance.updateRPC();
admins.save();
}); });
Net.handleServer(PositionPacket.class, (id, packet) -> { Net.handleServer(PositionPacket.class, (id, packet) -> {
@ -161,6 +162,22 @@ public class NetServer extends Module{
}); });
Net.handleServer(ShootPacket.class, (id, packet) -> { Net.handleServer(ShootPacket.class, (id, packet) -> {
TraceInfo info = admins.getTrace(Net.getConnection(id).address);
Weapon weapon = (Weapon)Upgrade.getByID(packet.weaponid);
float wtrc = 45f;
if(!Timers.get(info.ip + "-weapontrace", wtrc)){
info.fastShots ++;
}else{
if(info.fastShots - 2 > (int)(wtrc / (weapon.getReload() / 2f))){
Net.kickConnection(id, KickReason.kick);
}
info.fastShots = 0;
}
packet.playerid = connections.get(id).id; packet.playerid = connections.get(id).id;
Net.sendExcept(id, packet, SendMode.udp); Net.sendExcept(id, packet, SendMode.udp);
}); });
@ -182,6 +199,7 @@ public class NetServer extends Module{
admins.getTrace(Net.getConnection(id).address).lastBlockPlaced = block; admins.getTrace(Net.getConnection(id).address).lastBlockPlaced = block;
admins.getTrace(Net.getConnection(id).address).totalBlocksPlaced ++; admins.getTrace(Net.getConnection(id).address).totalBlocksPlaced ++;
admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlockPlaced ++;
Net.send(packet, SendMode.tcp); Net.send(packet, SendMode.tcp);
}); });
@ -196,6 +214,7 @@ public class NetServer extends Module{
if(block != null) { if(block != null) {
admins.getTrace(Net.getConnection(id).address).lastBlockBroken = block; admins.getTrace(Net.getConnection(id).address).lastBlockBroken = block;
admins.getTrace(Net.getConnection(id).address).totalBlocksBroken++; admins.getTrace(Net.getConnection(id).address).totalBlocksBroken++;
admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlocksBroken ++;
if (block.update || block.destructible) if (block.update || block.destructible)
admins.getTrace(Net.getConnection(id).address).structureBlocksBroken++; admins.getTrace(Net.getConnection(id).address).structureBlocksBroken++;
} }

View File

@ -7,177 +7,224 @@ import io.anuke.ucore.core.Settings;
public class Administration { public class Administration {
private Json json = new Json(); private Json json = new Json();
/**All player info. Maps UUIDs to info. This persists throughout restarts.*/
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
/**Maps UUIDs to trace infos. This is wiped when a player logs off.*/
private ObjectMap<String, TraceInfo> traceInfo = new ObjectMap<>();
private Array<String> bannedIPs = new Array<>(); private Array<String> bannedIPs = new Array<>();
private Array<String> bannedIDs = new Array<>();
private Array<String> admins = new Array<>();
private ObjectMap<String, String> ipNames = new ObjectMap<>();
private ObjectMap<String, String> idIPs = new ObjectMap<>();
private ObjectMap<String, TraceInfo> traces = new ObjectMap<>();
public Administration(){ public Administration(){
Settings.defaultList( Settings.defaults("playerInfo", "{}");
"bans", "{}", Settings.defaults("bannedIPs", "{}");
"bannedIDs", "{}",
"admins", "{}",
"knownIPs", "{}",
"knownIDs", "{}"
);
load(); load();
} }
public TraceInfo getTrace(String ip){ /**Call when a player joins to update their information here.*/
if(!traces.containsKey(ip)) traces.put(ip, new TraceInfo(ip)); public void updatePlayerJoined(String id, String ip, String name){
PlayerInfo info = getCreateInfo(id);
info.lastName = name;
info.lastIP = ip;
info.timesJoined ++;
if(!info.names.contains(name, false)) info.names.add(name);
if(!info.ips.contains(ip, false)) info.ips.add(ip);
}
return traces.get(ip); /**Returns trace info by IP.*/
public TraceInfo getTrace(String ip){
if(!traceInfo.containsKey(ip)) traceInfo.put(ip, new TraceInfo(ip));
return traceInfo.get(ip);
} }
public void clearTraces(){ public void clearTraces(){
traces.clear(); traceInfo.clear();
} }
/**Sets last known name for an IP.*/ /**Bans a player by IP; returns whether this player was already banned.
public void setKnownName(String ip, String name){ * If there are players who at any point had this IP, they will be UUID banned as well.*/
ipNames.put(ip, name);
saveKnown();
}
/**Sets last known UUID for an IP.*/
public void setKnownIP(String id, String ip){
idIPs.put(id, ip);
saveKnown();
}
/**Returns the last known name for an IP. Returns 'unknown' if this IP has an unknown username.*/
public String getLastName(String ip){
return ipNames.get(ip, "unknown");
}
/**Returns the last known IP for a UUID. Returns 'unknown' if this IP has an unknown IP.*/
public String getLastIP(String id){
return idIPs.get(id, "unknown");
}
/**Return the last known device ID associated with an IP. Returns 'unknown' if this IP has an unknown device.*/
public String getLastID(String ip){
for(String id : idIPs.keys()){
if(idIPs.get(id).equals(ip)){
return id;
}
}
return "unknown";
}
/**Returns list of banned IPs.*/
public Array<String> getBanned(){
return bannedIPs;
}
/**Returns list of banned IDs.*/
public Array<String> getBannedIDs(){
return bannedIDs;
}
/**Bans a player by IP; returns whether this player was already banned.*/
public boolean banPlayerIP(String ip){ public boolean banPlayerIP(String ip){
if(bannedIPs.contains(ip, false)) if(bannedIPs.contains(ip, false))
return false; return false;
for(PlayerInfo info : playerInfo.values()){
if(info.ips.contains(ip, false)){
info.banned = true;
}
}
bannedIPs.add(ip); bannedIPs.add(ip);
saveBans(); save();
return true; return true;
} }
/**Bans a player by UUID.*/ /**Bans a player by UUID; returns whether this player was already banned.*/
public boolean banPlayerID(String id){ public boolean banPlayerID(String id){
if(bannedIDs.contains(id, false)) if(playerInfo.containsKey(id) && playerInfo.get(id).banned)
return false; return false;
bannedIDs.add(id);
saveBans(); getCreateInfo(id).banned = true;
save();
return true; return true;
} }
/**Unbans a player by IP; returns whether this player was banned in the first place..*/ /**Unbans a player by IP; returns whether this player was banned in the first place.
* This method also unbans any player that was banned and had this IP.*/
public boolean unbanPlayerIP(String ip){ public boolean unbanPlayerIP(String ip){
if(!bannedIPs.contains(ip, false)) boolean found = bannedIPs.contains(ip, false);
return false;
for(PlayerInfo info : playerInfo.values()){
if(info.ips.contains(ip, false)){
info.banned = false;
found = true;
}
}
bannedIPs.removeValue(ip, false); bannedIPs.removeValue(ip, false);
saveBans();
return true; if(found) save();
return found;
} }
/**Unbans a player by IP; returns whether this player was banned in the first place..*/ /**Unbans a player by ID; returns whether this player was banned in the first place.
public boolean unbanPlayerID(String ip){ * This also unbans all IPs the player used.*/
if(!bannedIDs.contains(ip, false)) public boolean unbanPlayerID(String id){
PlayerInfo info = getCreateInfo(id);
if(!info.banned)
return false; return false;
bannedIDs.removeValue(ip, false);
saveBans(); info.banned = false;
bannedIPs.removeAll(info.ips, false);
save();
return true; return true;
} }
/**Returns list of banned IPs.*/ /**Returns list of all players with admin status*/
public Array<String> getAdmins(){ public Array<PlayerInfo> getAdmins(){
return admins; Array<PlayerInfo> result = new Array<>();
for(PlayerInfo info : playerInfo.values()){
if(info.admin){
result.add(info);
}
}
return result;
}
/**Returns list of all players with admin status*/
public Array<PlayerInfo> getBanned(){
Array<PlayerInfo> result = new Array<>();
for(PlayerInfo info : playerInfo.values()){
if(info.banned){
result.add(info);
}
}
return result;
}
/**Returns all banned IPs. This does not include the IPs of ID-banned players.*/
public Array<String> getBannedIPs(){
return bannedIPs;
} }
/**Makes a player an admin. Returns whether this player was already an admin.*/ /**Makes a player an admin. Returns whether this player was already an admin.*/
public boolean adminPlayer(String ip){ public boolean adminPlayer(String id){
if(admins.contains(ip, false)) PlayerInfo info = getCreateInfo(id);
if(info.admin)
return false; return false;
admins.add(ip);
saveAdmins(); info.admin = true;
save();
return true; return true;
} }
/**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/ /**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/
public boolean unAdminPlayer(String ip){ public boolean unAdminPlayer(String id){
if(!admins.contains(ip, false)) PlayerInfo info = getCreateInfo(id);
if(!info.admin)
return false; return false;
admins.removeValue(ip, false);
saveAdmins(); info.admin = false;
save();
return true; return true;
} }
public boolean isIPBanned(String ip){ public boolean isIPBanned(String ip){
return bannedIPs.contains(ip, false); return bannedIPs.contains(ip, false) || (findByIP(ip) != null && findByIP(ip).banned);
} }
public boolean isIDBanned(String uuid){ public boolean isIDBanned(String uuid){
return bannedIDs.contains(uuid, false); return getCreateInfo(uuid).banned;
} }
public boolean isAdmin(String ip){ public boolean isAdmin(String id){
return admins.contains(ip, false); return getCreateInfo(id).admin;
} }
private void saveKnown(){ public PlayerInfo getInfo(String id){
Settings.putString("knownIPs", json.toJson(ipNames)); return getCreateInfo(id);
Settings.putString("knownIDs", json.toJson(idIPs));
Settings.save();
} }
private void saveBans(){ public PlayerInfo getInfoOptional(String id){
Settings.putString("bans", json.toJson(bannedIPs)); return playerInfo.get(id);
Settings.putString("bannedIDs", json.toJson(bannedIDs));
Settings.save();
} }
private void saveAdmins(){ public PlayerInfo findByIP(String ip){
Settings.putString("admins", json.toJson(admins)); for(PlayerInfo info : playerInfo.values()){
if(info.ips.contains(ip, false)){
return info;
}
}
return null;
}
private PlayerInfo getCreateInfo(String id){
if(playerInfo.containsKey(id)){
return playerInfo.get(id);
}else{
PlayerInfo info = new PlayerInfo(id);
playerInfo.put(id, info);
save();
return info;
}
}
public void save(){
Settings.putString("playerInfo", json.toJson(playerInfo));
Settings.putString("bannedIPs", json.toJson(bannedIPs));
Settings.save(); Settings.save();
} }
private void load(){ private void load(){
bannedIPs = json.fromJson(Array.class, Settings.getString("bans")); playerInfo = json.fromJson(ObjectMap.class, Settings.getString("playerInfo"));
bannedIDs = json.fromJson(Array.class, Settings.getString("bannedIDs")); bannedIPs = json.fromJson(Array.class, Settings.getString("bannedIPs"));
admins = json.fromJson(Array.class, Settings.getString("admins")); }
ipNames = json.fromJson(ObjectMap.class, Settings.getString("knownIPs"));
idIPs = json.fromJson(ObjectMap.class, Settings.getString("knownIDs")); public static class PlayerInfo{
public String id;
public String lastName = "<unknown>", lastIP = "<unknown>";
public Array<String> ips = new Array<>();
public Array<String> names = new Array<>();
public int timesKicked; //TODO not implemented!
public int timesJoined;
public int totalBlockPlaced;
public int totalBlocksBroken;
public boolean banned, admin;
PlayerInfo(String id){
this.id = id;
}
private PlayerInfo(){}
} }
} }

View File

@ -9,6 +9,8 @@ public class TraceInfo {
public boolean modclient; public boolean modclient;
public boolean android; public boolean android;
public int fastShots;
public int totalBlocksBroken; public int totalBlocksBroken;
public int structureBlocksBroken; public int structureBlocksBroken;
public Block lastBlockBroken = Blocks.air; public Block lastBlockBroken = Blocks.air;

View File

@ -117,6 +117,10 @@ public class Weapon extends Upgrade{
Effects.sound(shootsound, x, y); Effects.sound(shootsound, x, y);
} }
public float getReload(){
return reload;
}
public void shoot(Player p, float x, float y, float angle){ public void shoot(Player p, float x, float y, float angle){
shootInternal(p, x, y, angle); shootInternal(p, x, y, angle);

View File

@ -1,6 +1,7 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection; import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.net.NetEvents;
@ -36,15 +37,15 @@ public class AdminsDialog extends FloatingDialog {
table.add("$text.server.admins.none"); table.add("$text.server.admins.none");
} }
for(String ip : netServer.admins.getAdmins()){ for(PlayerInfo info : netServer.admins.getAdmins()){
Table res = new Table("button"); Table res = new Table("button");
res.margin(14f); res.margin(14f);
res.labelWrap("[LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f); res.labelWrap("[LIGHT_GRAY]" + info.lastName).width(w - h - 24f);
res.add().growX(); res.add().growX();
res.addImageButton("icon-cancel", 14*3, () -> { res.addImageButton("icon-cancel", 14*3, () -> {
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> { ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
netServer.admins.unAdminPlayer(ip); netServer.admins.unAdminPlayer(info.id);
for(Player player : playerGroup.all()){ for(Player player : playerGroup.all()){
NetConnection c = Net.getConnection(player.clientid); NetConnection c = Net.getConnection(player.clientid);
if(c != null){ if(c != null){

View File

@ -1,5 +1,6 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.ucore.scene.ui.ScrollPane; import io.anuke.ucore.scene.ui.ScrollPane;
import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Table;
@ -33,15 +34,15 @@ public class BansDialog extends FloatingDialog {
table.add("$text.server.bans.none"); table.add("$text.server.bans.none");
} }
for(String ip : netServer.admins.getBanned()){ for(PlayerInfo info : netServer.admins.getBanned()){
Table res = new Table("button"); Table res = new Table("button");
res.margin(14f); res.margin(14f);
res.labelWrap("IP: [LIGHT_GRAY]" + ip + "\n[]Name: [LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f); res.labelWrap("IP: [LIGHT_GRAY]" + info.lastIP + "\n[]Name: [LIGHT_GRAY]" + info.lastName).width(w - h - 24f);
res.add().growX(); res.add().growX();
res.addImageButton("icon-cancel", 14*3, () -> { res.addImageButton("icon-cancel", 14*3, () -> {
ui.showConfirm("$text.confirm", "$text.confirmunban", () -> { ui.showConfirm("$text.confirm", "$text.confirmunban", () -> {
netServer.admins.unbanPlayerIP(ip); netServer.admins.unbanPlayerID(info.id);
setup(); setup();
}); });
}).size(h).pad(-14f); }).size(h).pad(-14f);

View File

@ -10,6 +10,7 @@ import io.anuke.mindustry.game.EventType.GameOverEvent;
import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.io.SaveIO; import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetConnection; import io.anuke.mindustry.net.NetConnection;
import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.net.NetEvents;
@ -126,7 +127,7 @@ public class ServerControl extends Module {
Log.info("Stopped server."); Log.info("Stopped server.");
}); });
handler.register("host", "<mapname> <mode>", "Open the server with a specific map.", arg -> { handler.register("host", "<mapname> [mode]", "Open the server with a specific map.", arg -> {
if(state.is(State.playing)){ if(state.is(State.playing)){
err("Already hosting. Type 'stop' to stop hosting first."); err("Already hosting. Type 'stop' to stop hosting first.");
return; return;
@ -146,7 +147,7 @@ public class ServerControl extends Module {
GameMode mode = null; GameMode mode = null;
try{ try{
mode = GameMode.valueOf(arg[1]); mode = arg.length < 2 ? GameMode.waves : GameMode.valueOf(arg[1]);
}catch (IllegalArgumentException e){ }catch (IllegalArgumentException e){
err("No gamemode '{0}' found.", arg[1]); err("No gamemode '{0}' found.", arg[1]);
return; return;
@ -306,26 +307,26 @@ public class ServerControl extends Module {
}); });
handler.register("bans", "List all banned IPs and IDs.", arg -> { handler.register("bans", "List all banned IPs and IDs.", arg -> {
Array<String> bans = netServer.admins.getBanned(); Array<PlayerInfo> bans = netServer.admins.getBanned();
if(bans.size == 0){ if(bans.size == 0){
Log.info("No IP-banned players have been found.");
}else{
Log.info("&lyBanned players [IP]:");
for(String string : bans){
Log.info(" &ly {0} / Last known name: '{1}'", string, netServer.admins.getLastName(string));
}
}
Array<String> idbans = netServer.admins.getBannedIDs();
if(idbans.size == 0){
Log.info("No ID-banned players have been found."); Log.info("No ID-banned players have been found.");
}else{ }else{
Log.info("&lmBanned players [ID]:"); Log.info("&lyBanned players [ID]:");
for(String string : idbans){ for(PlayerInfo info : bans){
Log.info(" &lm '{0}' / Last known name: '{1}' / Last known IP: '{2}'", string, Log.info(" &ly {0} / Last known name: '{1}'", info.id, info.lastName);
netServer.admins.getLastName(netServer.admins.getLastIP(string)), netServer.admins.getLastIP(string)); }
}
Array<String> ipbans = netServer.admins.getBannedIPs();
if(ipbans.size == 0){
Log.info("No IP-banned players have been found.");
}else{
Log.info("&lmBanned players [IP]:");
for(String string : ipbans){
PlayerInfo info = netServer.admins.findByIP(string);
Log.info(" &lm '{0}' / Last known name: '{1}' / ID: '{2}'", string, info.lastName, info.id);
} }
} }
}); });
@ -363,12 +364,6 @@ public class ServerControl extends Module {
handler.register("unbanip", "<ip>", "Completely unban a person by IP.", arg -> { handler.register("unbanip", "<ip>", "Completely unban a person by IP.", arg -> {
if(netServer.admins.unbanPlayerIP(arg[0])) { if(netServer.admins.unbanPlayerIP(arg[0])) {
info("Unbanned player by IP: {0}.", arg[0]); info("Unbanned player by IP: {0}.", arg[0]);
for(String s : netServer.admins.getBannedIDs()){
if(netServer.admins.getLastIP(s).equals(arg[0])){
netServer.admins.unbanPlayerID(s);
Log.info("Also unbanned UUID '{0}' as it corresponds to this IP.", s);
}
}
}else{ }else{
err("That IP is not banned!"); err("That IP is not banned!");
} }
@ -377,11 +372,6 @@ public class ServerControl extends Module {
handler.register("unbanid", "<id>", "Completely unban a person by ID.", arg -> { handler.register("unbanid", "<id>", "Completely unban a person by ID.", arg -> {
if(netServer.admins.unbanPlayerID(arg[0])) { if(netServer.admins.unbanPlayerID(arg[0])) {
info("&lmUnbanned player by ID: {0}.", arg[0]); info("&lmUnbanned player by ID: {0}.", arg[0]);
String ip = netServer.admins.getLastIP(arg[0]);
if(!ip.equals("unknown")) {
netServer.admins.unbanPlayerIP(ip);
Log.info("Also unbanned IP '{0}' as it corresponds to this ID.", ip);
}
}else{ }else{
err("That IP is not banned!"); err("That IP is not banned!");
} }
@ -403,10 +393,10 @@ public class ServerControl extends Module {
} }
if(target != null){ if(target != null){
String ip = Net.getConnection(target.clientid).address; String id = netServer.admins.getTrace(Net.getConnection(target.clientid).address).uuid;
netServer.admins.adminPlayer(ip); netServer.admins.adminPlayer(id);
NetEvents.handleAdminSet(target, true); NetEvents.handleAdminSet(target, true);
info("Admin-ed player by IP: {0} / {1}", ip, arg[0]); info("Admin-ed player by ID: {0} / {1}", id, arg[0]);
}else{ }else{
info("Nobody with that name could be found."); info("Nobody with that name could be found.");
} }
@ -428,24 +418,24 @@ public class ServerControl extends Module {
} }
if(target != null){ if(target != null){
String ip = Net.getConnection(target.clientid).address; String id = netServer.admins.getTrace(Net.getConnection(target.clientid).address).uuid;
netServer.admins.unAdminPlayer(ip); netServer.admins.unAdminPlayer(id);
NetEvents.handleAdminSet(target, false); NetEvents.handleAdminSet(target, false);
info("Un-admin-ed player by IP: {0} / {1}", ip, arg[0]); info("Un-admin-ed player by ID: {0} / {1}", id, arg[0]);
}else{ }else{
info("Nobody with that name could be found."); info("Nobody with that name could be found.");
} }
}); });
handler.register("admins", "List all admins.", arg -> { handler.register("admins", "List all admins.", arg -> {
Array<String> admins = netServer.admins.getAdmins(); Array<PlayerInfo> admins = netServer.admins.getAdmins();
if(admins.size == 0){ if(admins.size == 0){
Log.info("No admins have been found."); Log.info("No admins have been found.");
}else{ }else{
Log.info("&lyAdmins:"); Log.info("&lyAdmins:");
for(String string : admins){ for(PlayerInfo info : admins){
Log.info(" &luy {0} / Name: '{1}'", string, netServer.admins.getLastName(string)); Log.info(" &luy {0} / ID: '{1}' / IP: '{2}'", info.lastName, info.id, info.lastIP);
} }
} }
}); });
@ -509,7 +499,7 @@ public class ServerControl extends Module {
info("Core destroyed."); info("Core destroyed.");
}); });
handler.register("info", "Print debug info", arg -> { handler.register("debug", "Print debug info", arg -> {
info(DebugFragment.debugInfo()); info(DebugFragment.debugInfo());
}); });
@ -540,6 +530,23 @@ public class ServerControl extends Module {
} }
}); });
handler.register("info", "<UUID>", "Get global info for a player's UUID.", arg -> {
PlayerInfo info = netServer.admins.getInfoOptional(arg[0]);
if(info != null){
Log.info("&lcTrace info for player '{0}':", info.lastName);
Log.info(" &lyall names used: {0}", info.names);
Log.info(" &lyIP: {0}", info.lastIP);
Log.info(" &lyall IPs used: {0}", info.ips);
Log.info(" &lytimes joined: {0}", info.timesJoined);
Log.info("");
Log.info(" &lytotal blocks broken: {0}", info.totalBlocksBroken);
Log.info(" &lytotal blocks placed: {0}", info.totalBlockPlaced);
}else{
info("Nobody with that UUID could be found.");
}
});
handler.register("trace", "<username...>", "Trace a player's actions", arg -> { handler.register("trace", "<username...>", "Trace a player's actions", arg -> {
if(!state.is(State.playing)) { if(!state.is(State.playing)) {
err("Open the server first."); err("Open the server first.");