Merge branch 'master' of https://github.com/Anuken/Mindustry into 4.0
This commit is contained in:
@@ -38,6 +38,7 @@ text.server.kicked.invalidPassword=Invalid password!
|
||||
text.server.kicked.clientOutdated=Outdated client! Update your game!
|
||||
text.server.kicked.serverOutdated=Outdated server! Ask the host to update!
|
||||
text.server.kicked.banned=You are banned on this server.
|
||||
text.server.kicked.recentKick=You have been kicked recently.\nWait before connecting again.
|
||||
text.server.connected={0} has joined.
|
||||
text.server.disconnected={0} has disconnected.
|
||||
text.nohost=Can't host server on a custom map!
|
||||
@@ -262,7 +263,7 @@ setting.sensitivity.name=Controller Sensitivity
|
||||
setting.saveinterval.name=Autosave Interval
|
||||
setting.seconds={0} Seconds
|
||||
setting.fullscreen.name=Fullscreen
|
||||
setting.multithread.name=Multithreading [scarlet](unstable!)
|
||||
setting.multithread.name=Multithreading
|
||||
setting.fps.name=Show FPS
|
||||
setting.vsync.name=VSync
|
||||
setting.lasers.name=Show Power Lasers
|
||||
|
||||
@@ -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 = Спавн ворогів
|
||||
@@ -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=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=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=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=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=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
|
||||
|
||||
@@ -96,7 +96,7 @@ public class Vars{
|
||||
|
||||
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")};
|
||||
|
||||
public static final Color[] playerColors = {
|
||||
|
||||
@@ -8,10 +8,9 @@ import io.anuke.mindustry.entities.SyncEntity;
|
||||
import io.anuke.mindustry.game.EventType.GameOverEvent;
|
||||
import io.anuke.mindustry.io.Platform;
|
||||
import io.anuke.mindustry.io.Version;
|
||||
import io.anuke.mindustry.net.Administration;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.net.Administration.PlayerInfo;
|
||||
import io.anuke.mindustry.net.Net.SendMode;
|
||||
import io.anuke.mindustry.net.NetworkIO;
|
||||
import io.anuke.mindustry.net.Packets.*;
|
||||
import io.anuke.mindustry.resource.*;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
@@ -32,7 +31,7 @@ import java.nio.ByteBuffer;
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class NetServer extends Module{
|
||||
private final static float serverSyncTime = 4, itemSyncTime = 10;
|
||||
private final static float serverSyncTime = 4, itemSyncTime = 10, kickDuration = 30 * 1000;
|
||||
|
||||
private final static int timerEntitySync = 0;
|
||||
private final static int timerStateSync = 1;
|
||||
@@ -52,40 +51,48 @@ public class NetServer extends Module{
|
||||
|
||||
Net.handleServer(Connect.class, (id, connect) -> {
|
||||
if(admins.isIPBanned(connect.addressTCP)){
|
||||
Net.kickConnection(id, KickReason.banned);
|
||||
kick(id, KickReason.banned);
|
||||
}
|
||||
});
|
||||
|
||||
Net.handleServer(ConnectPacket.class, (id, packet) -> {
|
||||
String uuid = new String(Base64Coder.encode(packet.uuid));
|
||||
|
||||
if(Net.getConnection(id) == null ||
|
||||
admins.isIPBanned(Net.getConnection(id).address)) return;
|
||||
|
||||
TraceInfo trace = admins.getTrace(Net.getConnection(id).address);
|
||||
PlayerInfo info = admins.getInfo(uuid);
|
||||
|
||||
if(admins.isIDBanned(uuid)){
|
||||
Net.kickConnection(id, KickReason.banned);
|
||||
kick(id, KickReason.banned);
|
||||
return;
|
||||
}
|
||||
|
||||
if(TimeUtils.millis() - info.lastKicked < kickDuration){
|
||||
kick(id, KickReason.recentKick);
|
||||
return;
|
||||
}
|
||||
|
||||
String ip = Net.getConnection(id).address;
|
||||
|
||||
admins.setKnownName(ip, packet.name);
|
||||
admins.setKnownIP(uuid, ip);
|
||||
admins.getTrace(ip).uuid = uuid;
|
||||
admins.getTrace(ip).android = packet.android;
|
||||
admins.updatePlayerJoined(uuid, ip, packet.name);
|
||||
trace.uuid = uuid;
|
||||
trace.android = packet.android;
|
||||
|
||||
if(packet.version != Version.build && Version.build != -1 && packet.version != -1){
|
||||
Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||
kick(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||
return;
|
||||
}
|
||||
|
||||
if(packet.version == -1){
|
||||
admins.getTrace(ip).modclient = true;
|
||||
trace.modclient = true;
|
||||
}
|
||||
|
||||
Log.info("Sending data to player '{0}' / {1}", packet.name, id);
|
||||
|
||||
Player player = new Player();
|
||||
player.isAdmin = admins.isAdmin(Net.getConnection(id).address);
|
||||
player.isAdmin = admins.isAdmin(uuid, ip);
|
||||
player.clientid = id;
|
||||
player.name = packet.name;
|
||||
player.mech = packet.android ? Mech.standardShip : Mech.standard;
|
||||
@@ -95,7 +102,7 @@ public class NetServer extends Module{
|
||||
player.color.set(packet.color);
|
||||
connections.put(id, player);
|
||||
|
||||
admins.getTrace(ip).playerid = player.id;
|
||||
trace.playerid = player.id;
|
||||
|
||||
//TODO try DeflaterOutputStream
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
@@ -137,6 +144,7 @@ public class NetServer extends Module{
|
||||
Net.send(dc, SendMode.tcp);
|
||||
|
||||
Platform.instance.updateRPC();
|
||||
admins.save();
|
||||
});
|
||||
|
||||
Net.handleServer(PositionPacket.class, (id, packet) -> {
|
||||
@@ -149,9 +157,23 @@ public class NetServer extends Module{
|
||||
BulletType type = BaseBulletType.getByID(packet.bulletid);
|
||||
|
||||
player.onRemoteShoot(type, packet.x, packet.y, packet.rotation, packet.data);
|
||||
TraceInfo info = admins.getTrace(Net.getConnection(id).address);
|
||||
Weapon weapon = Upgrade.getByID((byte)packet.data);
|
||||
|
||||
packet.entityid = player.id;
|
||||
packet.groupid = (byte)player.getGroup().getID();
|
||||
float wtrc = 40f;
|
||||
|
||||
if(!Timers.get(info.ip + "-weapontrace", wtrc)){
|
||||
info.fastShots ++;
|
||||
}else{
|
||||
|
||||
if(info.fastShots - 2 > (int)(wtrc / (weapon.getReload() / 2f))){
|
||||
kick(id, KickReason.kick);
|
||||
}
|
||||
|
||||
info.fastShots = 0;
|
||||
}
|
||||
|
||||
packet.entityid = connections.get(id).id;
|
||||
Net.sendExcept(id, packet, SendMode.udp);
|
||||
});
|
||||
|
||||
@@ -173,6 +195,7 @@ public class NetServer extends Module{
|
||||
|
||||
admins.getTrace(Net.getConnection(id).address).lastBlockPlaced = block;
|
||||
admins.getTrace(Net.getConnection(id).address).totalBlocksPlaced ++;
|
||||
admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlockPlaced ++;
|
||||
|
||||
Net.send(packet, SendMode.tcp);
|
||||
});
|
||||
@@ -188,6 +211,7 @@ public class NetServer extends Module{
|
||||
if(block != null) {
|
||||
admins.getTrace(Net.getConnection(id).address).lastBlockBroken = block;
|
||||
admins.getTrace(Net.getConnection(id).address).totalBlocksBroken++;
|
||||
admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlocksBroken ++;
|
||||
if (block.update || block.destructible)
|
||||
admins.getTrace(Net.getConnection(id).address).structureBlocksBroken++;
|
||||
}
|
||||
@@ -270,10 +294,10 @@ public class NetServer extends Module{
|
||||
|
||||
if(packet.action == AdminAction.ban){
|
||||
admins.banPlayerIP(ip);
|
||||
Net.kickConnection(other.clientid, KickReason.banned);
|
||||
kick(other.clientid, KickReason.banned);
|
||||
Log.info("&lc{0} has banned {1}.", player.name, other.name);
|
||||
}else if(packet.action == AdminAction.kick){
|
||||
Net.kickConnection(other.clientid, KickReason.kick);
|
||||
kick(other.clientid, KickReason.kick);
|
||||
Log.info("&lc{0} has kicked {1}.", player.name, other.name);
|
||||
}else if(packet.action == AdminAction.trace){
|
||||
TracePacket trace = new TracePacket();
|
||||
@@ -306,6 +330,31 @@ public class NetServer extends Module{
|
||||
admins.clearTraces();
|
||||
}
|
||||
|
||||
public void kick(int connection, KickReason reason){
|
||||
NetConnection con = Net.getConnection(connection);
|
||||
if(con == null){
|
||||
Log.err("Cannot kick unknown player!");
|
||||
return;
|
||||
}else{
|
||||
Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason);
|
||||
}
|
||||
|
||||
PlayerInfo info = admins.getInfo(admins.getTrace(con.address).uuid);
|
||||
|
||||
if(reason == KickReason.kick || reason == KickReason.banned){
|
||||
info.timesKicked ++;
|
||||
info.lastKicked = TimeUtils.millis();
|
||||
}
|
||||
|
||||
KickPacket p = new KickPacket();
|
||||
p.reason = reason;
|
||||
|
||||
con.send(p, SendMode.tcp);
|
||||
Timers.runTask(2f, con::close);
|
||||
|
||||
admins.save();
|
||||
}
|
||||
|
||||
void sync(){
|
||||
|
||||
if(timer.get(timerEntitySync, serverSyncTime)){
|
||||
|
||||
@@ -156,12 +156,12 @@ public class UI extends SceneModule{
|
||||
language = new LanguageDialog();
|
||||
settings = new SettingsMenuDialog();
|
||||
paused = new PausedDialog();
|
||||
changelog = new ChangelogDialog();
|
||||
about = new AboutDialog();
|
||||
host = new HostDialog();
|
||||
bans = new BansDialog();
|
||||
admins = new AdminsDialog();
|
||||
traces = new TraceDialog();
|
||||
changelog = new ChangelogDialog();
|
||||
|
||||
build.begin(scene);
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public class DefaultKeybinds {
|
||||
"block_info", Input.CONTROL_LEFT,
|
||||
"player_list", Input.TAB,
|
||||
"chat", Input.ENTER,
|
||||
"chat_scroll_up", Input.UP,
|
||||
"chat_scroll_down", Input.DOWN,
|
||||
"console", Input.GRAVE,
|
||||
"weapon_1", Input.NUM_1,
|
||||
"weapon_2", Input.NUM_2,
|
||||
@@ -53,6 +55,8 @@ public class DefaultKeybinds {
|
||||
"rotate", new Axis(Input.CONTROLLER_A, Input.CONTROLLER_B),
|
||||
"player_list", Input.CONTROLLER_START,
|
||||
"chat", Input.ENTER,
|
||||
"chat_scroll_up", Input.UP,
|
||||
"chat_scroll_down", Input.DOWN,
|
||||
"console", Input.GRAVE,
|
||||
"weapon_1", Input.NUM_1,
|
||||
"weapon_2", Input.NUM_2,
|
||||
|
||||
@@ -107,6 +107,10 @@ public class DesktopInput extends InputHandler{
|
||||
Cursors.restoreCursor();
|
||||
}
|
||||
}
|
||||
|
||||
if(target != null && target.block().isConfigurable(target)){
|
||||
showCursor = true;
|
||||
}
|
||||
|
||||
if(target != null && Inputs.keyTap("select") && !ui.hasMouse()){
|
||||
if(target.block().isConfigurable(target)){
|
||||
|
||||
@@ -116,19 +116,23 @@ public abstract class InputHandler extends InputAdapter{
|
||||
}
|
||||
|
||||
public void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
||||
if(!Net.client()){
|
||||
if(!Net.client()){ //is server or singleplayer
|
||||
Placement.placeBlock(player.team, x, y, result, rotation, effects, sound);
|
||||
Tile tile = world.tile(x, y);
|
||||
if(tile != null) result.placed(tile);
|
||||
}
|
||||
|
||||
if(Net.active()){
|
||||
NetEvents.handlePlace(x, y, result, rotation);
|
||||
}
|
||||
|
||||
if(!Net.client()){
|
||||
Tile tile = world.tile(x, y);
|
||||
if(tile != null) result.placed(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public void breakBlock(int x, int y, boolean sound){
|
||||
if(!Net.client()) Placement.breakBlock(player.team, x, y, true, sound);
|
||||
if(!Net.client())
|
||||
Placement.breakBlock(player.team, x, y, true, sound);
|
||||
|
||||
if(Net.active()){
|
||||
NetEvents.handleBreak(x, y);
|
||||
|
||||
@@ -7,177 +7,228 @@ import io.anuke.ucore.core.Settings;
|
||||
|
||||
public class Administration {
|
||||
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> 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(){
|
||||
Settings.defaultList(
|
||||
"bans", "{}",
|
||||
"bannedIDs", "{}",
|
||||
"admins", "{}",
|
||||
"knownIPs", "{}",
|
||||
"knownIDs", "{}"
|
||||
);
|
||||
Settings.defaults("playerInfo", "{}");
|
||||
Settings.defaults("bannedIPs", "{}");
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
public TraceInfo getTrace(String ip){
|
||||
if(!traces.containsKey(ip)) traces.put(ip, new TraceInfo(ip));
|
||||
/**Call when a player joins to update their information here.*/
|
||||
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(){
|
||||
traces.clear();
|
||||
traceInfo.clear();
|
||||
}
|
||||
|
||||
/**Sets last known name for an IP.*/
|
||||
public void setKnownName(String ip, String name){
|
||||
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.*/
|
||||
/**Bans a player by IP; returns whether this player was already banned.
|
||||
* If there are players who at any point had this IP, they will be UUID banned as well.*/
|
||||
public boolean banPlayerIP(String ip){
|
||||
if(bannedIPs.contains(ip, false))
|
||||
return false;
|
||||
|
||||
for(PlayerInfo info : playerInfo.values()){
|
||||
if(info.ips.contains(ip, false)){
|
||||
info.banned = true;
|
||||
}
|
||||
}
|
||||
|
||||
bannedIPs.add(ip);
|
||||
saveBans();
|
||||
save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**Bans a player by UUID.*/
|
||||
/**Bans a player by UUID; returns whether this player was already banned.*/
|
||||
public boolean banPlayerID(String id){
|
||||
if(bannedIDs.contains(id, false))
|
||||
if(playerInfo.containsKey(id) && playerInfo.get(id).banned)
|
||||
return false;
|
||||
bannedIDs.add(id);
|
||||
saveBans();
|
||||
|
||||
getCreateInfo(id).banned = true;
|
||||
|
||||
save();
|
||||
|
||||
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){
|
||||
if(!bannedIPs.contains(ip, false))
|
||||
return false;
|
||||
boolean found = bannedIPs.contains(ip, false);
|
||||
|
||||
for(PlayerInfo info : playerInfo.values()){
|
||||
if(info.ips.contains(ip, false)){
|
||||
info.banned = false;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
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..*/
|
||||
public boolean unbanPlayerID(String ip){
|
||||
if(!bannedIDs.contains(ip, false))
|
||||
/**Unbans a player by ID; returns whether this player was banned in the first place.
|
||||
* This also unbans all IPs the player used.*/
|
||||
public boolean unbanPlayerID(String id){
|
||||
PlayerInfo info = getCreateInfo(id);
|
||||
|
||||
if(!info.banned)
|
||||
return false;
|
||||
bannedIDs.removeValue(ip, false);
|
||||
saveBans();
|
||||
|
||||
info.banned = false;
|
||||
bannedIPs.removeAll(info.ips, false);
|
||||
save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**Returns list of banned IPs.*/
|
||||
public Array<String> getAdmins(){
|
||||
return admins;
|
||||
/**Returns list of all players with admin status*/
|
||||
public Array<PlayerInfo> getAdmins(){
|
||||
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.*/
|
||||
public boolean adminPlayer(String ip){
|
||||
if(admins.contains(ip, false))
|
||||
public boolean adminPlayer(String id, String ip){
|
||||
PlayerInfo info = getCreateInfo(id);
|
||||
|
||||
if(info.admin)
|
||||
return false;
|
||||
admins.add(ip);
|
||||
saveAdmins();
|
||||
|
||||
info.validAdminIP = ip;
|
||||
info.admin = true;
|
||||
save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/
|
||||
public boolean unAdminPlayer(String ip){
|
||||
if(!admins.contains(ip, false))
|
||||
public boolean unAdminPlayer(String id){
|
||||
PlayerInfo info = getCreateInfo(id);
|
||||
|
||||
if(!info.admin)
|
||||
return false;
|
||||
admins.removeValue(ip, false);
|
||||
saveAdmins();
|
||||
|
||||
info.admin = false;
|
||||
save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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){
|
||||
return bannedIDs.contains(uuid, false);
|
||||
return getCreateInfo(uuid).banned;
|
||||
}
|
||||
|
||||
public boolean isAdmin(String ip){
|
||||
return admins.contains(ip, false);
|
||||
public boolean isAdmin(String id, String ip){
|
||||
PlayerInfo info = getCreateInfo(id);
|
||||
return info.admin && ip.equals(info.validAdminIP);
|
||||
}
|
||||
|
||||
private void saveKnown(){
|
||||
Settings.putString("knownIPs", json.toJson(ipNames));
|
||||
Settings.putString("knownIDs", json.toJson(idIPs));
|
||||
Settings.save();
|
||||
public PlayerInfo getInfo(String id){
|
||||
return getCreateInfo(id);
|
||||
}
|
||||
|
||||
private void saveBans(){
|
||||
Settings.putString("bans", json.toJson(bannedIPs));
|
||||
Settings.putString("bannedIDs", json.toJson(bannedIDs));
|
||||
Settings.save();
|
||||
public PlayerInfo getInfoOptional(String id){
|
||||
return playerInfo.get(id);
|
||||
}
|
||||
|
||||
private void saveAdmins(){
|
||||
Settings.putString("admins", json.toJson(admins));
|
||||
public PlayerInfo findByIP(String ip){
|
||||
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();
|
||||
}
|
||||
|
||||
private void load(){
|
||||
bannedIPs = json.fromJson(Array.class, Settings.getString("bans"));
|
||||
bannedIDs = json.fromJson(Array.class, Settings.getString("bannedIDs"));
|
||||
admins = json.fromJson(Array.class, Settings.getString("admins"));
|
||||
ipNames = json.fromJson(ObjectMap.class, Settings.getString("knownIPs"));
|
||||
idIPs = json.fromJson(ObjectMap.class, Settings.getString("knownIDs"));
|
||||
playerInfo = json.fromJson(ObjectMap.class, Settings.getString("playerInfo"));
|
||||
bannedIPs = json.fromJson(Array.class, Settings.getString("bannedIPs"));
|
||||
}
|
||||
|
||||
public static class PlayerInfo{
|
||||
public String id;
|
||||
public String lastName = "<unknown>", lastIP = "<unknown>";
|
||||
public String validAdminIP;
|
||||
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;
|
||||
public long lastKicked; //last kicked timestamp
|
||||
|
||||
PlayerInfo(String id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private PlayerInfo(){}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.badlogic.gdx.utils.reflect.ClassReflection;
|
||||
import io.anuke.mindustry.io.Platform;
|
||||
import io.anuke.mindustry.net.Packet.ImportantPacket;
|
||||
import io.anuke.mindustry.net.Packet.UnimportantPacket;
|
||||
import io.anuke.mindustry.net.Packets.KickReason;
|
||||
import io.anuke.mindustry.net.Streamable.StreamBegin;
|
||||
import io.anuke.mindustry.net.Streamable.StreamBuilder;
|
||||
import io.anuke.mindustry.net.Streamable.StreamChunk;
|
||||
@@ -101,11 +100,6 @@ public class Net{
|
||||
clientProvider.discover(cons);
|
||||
}
|
||||
|
||||
/**Kick a specified connection from the server.*/
|
||||
public static void kickConnection(int id, KickReason reason){
|
||||
serverProvider.kick(id, reason);
|
||||
}
|
||||
|
||||
/**Returns a list of all connections IDs.*/
|
||||
public static Array<NetConnection> getConnections(){
|
||||
return (Array<NetConnection>)serverProvider.getConnections();
|
||||
@@ -307,10 +301,6 @@ public class Net{
|
||||
Array<? extends NetConnection> getConnections();
|
||||
/**Returns a connection by ID.*/
|
||||
NetConnection getByID(int id);
|
||||
/**Kick a certain connection.*/
|
||||
void kick(int connection, KickReason reason);
|
||||
/**Returns the ping for a certain connection.*/
|
||||
int getPingFor(NetConnection connection);
|
||||
/**Close all connections.*/
|
||||
void dispose();
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ public class Packets {
|
||||
}
|
||||
|
||||
public enum KickReason{
|
||||
kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true);
|
||||
kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true), recentKick;
|
||||
public final boolean quiet;
|
||||
|
||||
KickReason(){ quiet = false; }
|
||||
|
||||
@@ -9,6 +9,8 @@ public class TraceInfo {
|
||||
public boolean modclient;
|
||||
public boolean android;
|
||||
|
||||
public int fastShots;
|
||||
|
||||
public int totalBlocksBroken;
|
||||
public int structureBlocksBroken;
|
||||
public Block lastBlockBroken = Blocks.air;
|
||||
|
||||
@@ -117,6 +117,10 @@ public class Weapon extends Upgrade{
|
||||
Effects.sound(shootsound, x, y);
|
||||
}
|
||||
|
||||
public float getReload(){
|
||||
return reload;
|
||||
}
|
||||
|
||||
public void shoot(Player p, float x, float y, float angle){
|
||||
shootInternal(p, x, y, angle);
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.anuke.mindustry.ui.dialogs;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.ui.Links;
|
||||
import io.anuke.mindustry.ui.Links.LinkEntry;
|
||||
import io.anuke.ucore.core.Core;
|
||||
@@ -10,6 +9,8 @@ import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||
import io.anuke.ucore.scene.ui.layout.Table;
|
||||
|
||||
import static io.anuke.mindustry.Vars.ui;
|
||||
|
||||
public class AboutDialog extends FloatingDialog {
|
||||
|
||||
public AboutDialog(){
|
||||
@@ -45,7 +46,7 @@ public class AboutDialog extends FloatingDialog {
|
||||
|
||||
table.addImageButton("icon-link", 14*3, () -> {
|
||||
if(!Gdx.net.openURI(link.link)){
|
||||
Vars.ui.showError("$text.linkfail");
|
||||
ui.showError("$text.linkfail");
|
||||
Gdx.app.getClipboard().setContents(link.link);
|
||||
}
|
||||
}).size(h-5, h);
|
||||
@@ -58,6 +59,7 @@ public class AboutDialog extends FloatingDialog {
|
||||
content().add(pane).growX();
|
||||
|
||||
buttons().addButton("$text.credits", this::showCredits).size(200f, 64f);
|
||||
buttons().addButton("$text.changelog.title", ui.changelog::show).size(200f, 64f);
|
||||
}
|
||||
|
||||
private void showCredits(){
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.anuke.mindustry.ui.dialogs;
|
||||
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.net.Administration.PlayerInfo;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.net.NetConnection;
|
||||
import io.anuke.mindustry.net.NetEvents;
|
||||
@@ -36,15 +37,15 @@ public class AdminsDialog extends FloatingDialog {
|
||||
table.add("$text.server.admins.none");
|
||||
}
|
||||
|
||||
for(String ip : netServer.admins.getAdmins()){
|
||||
for(PlayerInfo info : netServer.admins.getAdmins()){
|
||||
Table res = new Table("button");
|
||||
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.addImageButton("icon-cancel", 14*3, () -> {
|
||||
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
|
||||
netServer.admins.unAdminPlayer(ip);
|
||||
netServer.admins.unAdminPlayer(info.id);
|
||||
for(Player player : playerGroup.all()){
|
||||
NetConnection c = Net.getConnection(player.clientid);
|
||||
if(c != null){
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.layout.Table;
|
||||
|
||||
@@ -33,15 +34,15 @@ public class BansDialog extends FloatingDialog {
|
||||
table.add("$text.server.bans.none");
|
||||
}
|
||||
|
||||
for(String ip : netServer.admins.getBanned()){
|
||||
for(PlayerInfo info : netServer.admins.getBanned()){
|
||||
Table res = new Table("button");
|
||||
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.addImageButton("icon-cancel", 14*3, () -> {
|
||||
ui.showConfirm("$text.confirm", "$text.confirmunban", () -> {
|
||||
netServer.admins.unbanPlayerIP(ip);
|
||||
netServer.admins.unbanPlayerID(info.id);
|
||||
setup();
|
||||
});
|
||||
}).size(h).pad(-14f);
|
||||
|
||||
@@ -31,7 +31,7 @@ public class BackgroundFragment implements Fragment {
|
||||
float logoh = logo.getRegionHeight()*logoscl;
|
||||
|
||||
Draw.color();
|
||||
Core.batch.draw(logo, w/2 - logow/2, h - logoh + 15 - Unit.dp.scl(portrait ? 30f : 30), logow, logoh);
|
||||
Core.batch.draw(logo, w/2 - logow/2, h - logoh + 15 - Unit.dp.scl(portrait ? 30f : 0), logow, logoh);
|
||||
}).visible(() -> state.is(State.menu)).grow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import io.anuke.ucore.scene.ui.TextField;
|
||||
import io.anuke.ucore.scene.ui.layout.Table;
|
||||
import io.anuke.ucore.scene.ui.layout.Unit;
|
||||
import io.anuke.ucore.util.Log;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static io.anuke.mindustry.Vars.state;
|
||||
import static io.anuke.ucore.core.Core.scene;
|
||||
@@ -39,6 +40,8 @@ public class ChatFragment extends Table implements Fragment{
|
||||
private float textWidth = Unit.dp.scl(600);
|
||||
private Color shadowColor = new Color(0, 0, 0, 0.4f);
|
||||
private float textspacing = Unit.dp.scl(10);
|
||||
private Array<String> history = new Array<String>();
|
||||
private int historyPos = 0;
|
||||
|
||||
public ChatFragment(){
|
||||
super();
|
||||
@@ -57,8 +60,21 @@ public class ChatFragment extends Table implements Fragment{
|
||||
if(Net.active() && Inputs.keyTap("chat")){
|
||||
toggle();
|
||||
}
|
||||
|
||||
if (chatOpen) {
|
||||
if (Inputs.keyTap("chat_scroll_up") && historyPos < history.size - 1) {
|
||||
if (historyPos == 0) history.set(0, chatfield.getText());
|
||||
historyPos++;
|
||||
updateChat();
|
||||
}
|
||||
if (Inputs.keyTap("chat_scroll_down") && historyPos > 0) {
|
||||
historyPos--;
|
||||
updateChat();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
history.insert(0, "");
|
||||
setup();
|
||||
}
|
||||
|
||||
@@ -69,6 +85,8 @@ public class ChatFragment extends Table implements Fragment{
|
||||
|
||||
public void clearMessages(){
|
||||
messages.clear();
|
||||
history.clear();
|
||||
history.insert(0, "");
|
||||
}
|
||||
|
||||
private void setup(){
|
||||
@@ -144,7 +162,8 @@ public class ChatFragment extends Table implements Fragment{
|
||||
|
||||
private void sendMessage(){
|
||||
String message = chatfield.getText();
|
||||
chatfield.clearText();
|
||||
clearChatInput();
|
||||
history.insert(1, message);
|
||||
|
||||
if(message.replaceAll(" ", "").isEmpty()) return;
|
||||
|
||||
@@ -170,6 +189,17 @@ public class ChatFragment extends Table implements Fragment{
|
||||
public void hide(){
|
||||
scene.setKeyboardFocus(null);
|
||||
chatOpen = false;
|
||||
clearChatInput();
|
||||
}
|
||||
|
||||
public void updateChat() {
|
||||
chatfield.setText(history.get(historyPos));
|
||||
chatfield.setCursorPosition(chatfield.getText().length());
|
||||
}
|
||||
|
||||
public void clearChatInput() {
|
||||
historyPos = 0;
|
||||
history.set(0, "");
|
||||
chatfield.setText("");
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ public class PlayerListFragment implements Fragment{
|
||||
ui.showConfirm("$text.confirm", "$text.confirmban", () -> {
|
||||
if(Net.server()) {
|
||||
netServer.admins.banPlayerIP(connection.address);
|
||||
Net.kickConnection(player.clientid, KickReason.banned);
|
||||
netServer.kick(player.clientid, KickReason.banned);
|
||||
}else{
|
||||
NetEvents.handleAdministerRequest(player, AdminAction.ban);
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public class PlayerListFragment implements Fragment{
|
||||
|
||||
t.addImageButton("icon-cancel", 14*2, () -> {
|
||||
if(Net.server()) {
|
||||
Net.kickConnection(player.clientid, KickReason.kick);
|
||||
netServer.kick(player.clientid, KickReason.kick);
|
||||
}else{
|
||||
NetEvents.handleAdministerRequest(player, AdminAction.kick);
|
||||
}
|
||||
@@ -148,14 +148,16 @@ public class PlayerListFragment implements Fragment{
|
||||
t.addImageButton("icon-admin", "toggle", 14*2, () -> {
|
||||
if(Net.client()) return;
|
||||
|
||||
if(netServer.admins.isAdmin(connection.address)){
|
||||
String id = netServer.admins.getTrace(connection.address).uuid;
|
||||
|
||||
if(netServer.admins.isAdmin(id, connection.address)){
|
||||
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
|
||||
netServer.admins.unAdminPlayer(connection.address);
|
||||
netServer.admins.unAdminPlayer(id);
|
||||
NetEvents.handleAdminSet(player, false);
|
||||
});
|
||||
}else{
|
||||
ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> {
|
||||
netServer.admins.adminPlayer(connection.address);
|
||||
netServer.admins.adminPlayer(id, connection.address);
|
||||
NetEvents.handleAdminSet(player, true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class Teleporter extends PowerBlock{
|
||||
@Override
|
||||
public void placed(Tile tile){
|
||||
tile.<TeleporterEntity>entity().color = lastColor;
|
||||
Timers.run(1f, () -> setConfigure(tile, lastColor));
|
||||
setConfigure(tile, lastColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user