Compare commits

..

62 Commits
v39 ... v42

Author SHA1 Message Date
Anuken
08690b9d9e Merge pull request #133 from cloud9350/patch-4
fix fatal language translate (bundle ko)
2018-06-18 23:10:07 -04:00
Gureumi
d3eb9c9d99 Update bundle_ko.properties 2018-06-19 10:18:32 +09:00
Gureumi
a0ef3a0e44 fix fatal language translate (bundle ko)
fix no show server version + host user name on list
2018-06-19 10:13:07 +09:00
Anuken
ca3bfa2239 Merge remote-tracking branch 'origin/master' 2018-06-01 18:25:39 -04:00
Anuken
35a3a89d2d Changed Discord RPC version to 2.0.0 2018-06-01 18:25:23 -04:00
Anuken
a647efe3fd Merge pull request #125 from hellomouse/feature-blocklogsandrollback
Added edit logging and whole world rollback
2018-06-01 11:21:48 -06:00
Anuken
c79c23899f Merge branch 'master' into feature-blocklogsandrollback 2018-06-01 11:21:34 -06:00
Anuken
fcb74d4fba Removed additional info from mac version 2018-05-28 16:55:46 -04:00
Anuken
e58194ac3c Confined Mac users to Downloads/ directory 2018-05-28 12:34:01 -04:00
Anuken
31f83dfb40 Merge remote-tracking branch 'origin/master' 2018-05-25 01:10:31 -04:00
Anuken
022b16c8e6 Updated Platform.java 2018-05-25 01:10:12 -04:00
Anuken
d462d9c89b Removed JavaFX usage 2018-05-25 01:09:47 -04:00
Anuken
86360049c3 Updated FileChooser dir 2018-05-25 01:09:18 -04:00
Anuken
f524be6738 Added native file choosers for importing/exporting save 2018-05-24 23:16:03 -04:00
Anuken
519d2c1714 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	core/assets/sprites/icon.icns
2018-05-24 21:14:10 -04:00
Anuken
c19fcb831f Added native file chooser, fixed macOS prefs errors 2018-05-24 21:13:31 -04:00
Anuken
ff6268e616 Updated icns file 2018-05-24 21:12:41 -04:00
Anuken
b0f1ad63ee Changed prefs directory definition 2018-05-24 17:20:37 -04:00
Anuken
a7b017f307 Merge remote-tracking branch 'origin/master' 2018-05-24 16:13:19 -04:00
Anuken
29c3df8ba7 Changed bundle identifier script 2018-05-24 16:12:12 -04:00
Anuken
ba0c7907ca Merge remote-tracking branch 'origin/master' 2018-05-24 11:21:16 -04:00
Anuken
4c6ae83d4b Build 40 release 2018-05-24 11:19:03 -04:00
Anuken
b4088dcfbc Some more revisions to iOS version 2018-05-22 14:30:54 -07:00
Anuken
d70dd05221 Fixed 'permission denied' open error on iOS 2018-05-21 15:06:20 -04:00
Anuken
433cb99eab Removed outdated 3.2 itch.io GIF from readme 2018-05-21 12:05:02 -04:00
Anuken
3b0c66429c Fixed dedicated server crash 2018-05-21 11:27:06 -04:00
Anuken
ed232248a2 Updated polish bundle 2018-05-20 16:05:24 -04:00
Anuken
e215324292 Updated spanish bundle, added missing formatted text 2018-05-20 15:50:01 -04:00
Anuken
a8bc2f10e4 Fixed iPad share crash, updated iOS version to 3.5 2018-05-20 12:27:23 -07:00
Anuken
b64cbc223f Updated icons again 2018-05-20 15:16:44 -04:00
Anuken
94896451ac Merge remote-tracking branch 'origin/master' 2018-05-20 11:47:32 -04:00
Anuken
0e35bb4852 Fixed scrolling player menu causing panning on mobile 2018-05-20 11:47:20 -04:00
Anuken
cd30b1d9b3 Fixed bad scaling on ipad devices 2018-05-20 08:40:36 -07:00
Anuken
8d66d43a98 Some more sharing revisions 2018-05-19 20:19:47 -07:00
Anuken
8ca861360b Changed sharing mechanism again 2018-05-19 21:06:12 -04:00
Anuken
410db16160 Fixed iOS app errors 2018-05-19 17:52:29 -07:00
Anuken
1c1df81341 Added sharing for iOS saves, fixed icons 2018-05-19 17:13:04 -07:00
Anuken
66a1cc7b71 Updated iOS map sharing 2018-05-19 18:25:20 -04:00
Anuken
0543569315 Merge remote-tracking branch 'origin/master' 2018-05-19 15:01:55 -07:00
Anuken
adfebdb3f6 Merge pull request #122 from Prosta4okua/patch-1
Update bundle_ru.properties[Work in progress]
2018-05-19 15:58:41 -06:00
Prosta4okua
dbe06b0a93 Update bundle_ru.properties 2018-05-19 22:50:50 +03:00
Prosta4okua
b2ea733b45 Update bundle_ru.properties 2018-05-19 21:49:29 +03:00
Anuken
fc320fb9ee Moved Platform.java, basic iOS image loading 2018-05-19 10:35:48 -07:00
Commodore64x
a3268eefbf Fixed field size in rollback menu. 2018-05-19 14:34:50 +10:00
Commodore64x
5a77eebb34 Fixed ui in the rollback menu. 2018-05-19 14:22:07 +10:00
Commodore64x
68af32bc9f Added rollback into the admin menu, fixed a bug where players would crash upon trying to view their own logs on relog. 2018-05-19 13:37:44 +10:00
Anuken
2d768990ec Updated iOS icons 2018-05-18 07:53:30 -07:00
Anuken
10c44d0a4e Changed iOS version to 9.0.0 2018-05-17 22:31:26 -07:00
Anuken
57b0e3e311 Fixed iOS donate button and changelog appearing 2018-05-17 20:55:29 -07:00
Anuken
e45e04a6dc More ios fixes 2018-05-16 21:13:57 -07:00
Anuken
cf1148e7c8 iOS plist fixes, re-added music 2018-05-16 11:06:15 -07:00
Commodore64x
380a955e7a Updated the way packets are exchanged for edit logs. 2018-05-15 16:36:39 +10:00
Commodore64x
b3adf7b331 Replaced ArrayLists with Arrays, overhauled requesting of logs from the server, improved rollback command and added log resetting. 2018-05-14 18:48:44 +10:00
Commodore64x
7f2c2d9d6b Added edit logging and whole world rollback 2018-05-12 18:34:44 +10:00
Anuken
26025a3018 Fixed multithreading affecting wave time 2018-05-09 16:58:53 -07:00
Anuken
61e9611bab Merge pull request #124 from cloud9350/patch-2 2018-05-09 10:55:40 -04:00
Gureumi
cbdf252fe6 fix missing tranlate lang and update about info 2018-05-09 20:33:53 +09:00
Prosta4okua
665f0ee03e Update bundle_ru.properties 2018-05-06 09:45:17 +03:00
Anuken
b020336f8c Temporarily disabled validating replacements 2018-05-05 22:26:59 -04:00
Anuken
299c2af59b Added configurable anti-grief 2018-05-05 20:20:34 -04:00
Anuken
37a73eda37 Fixed menu opening and closing when chat is open 2018-05-05 18:36:25 -04:00
Anuken
3938022002 Updated build.gradle to fix invalid JVM args for windows 2018-05-05 15:47:18 -04:00
97 changed files with 1170 additions and 253 deletions

1
.gitignore vendored
View File

@@ -9,6 +9,7 @@
/desktop/mindustry-maps/ /desktop/mindustry-maps/
/desktop/gifexport/ /desktop/gifexport/
/core/lib/ /core/lib/
/annotations/build/
/kryonet/build/ /kryonet/build/
/server/build/ /server/build/
/android/assets/mindustry-maps/ /android/assets/mindustry-maps/

View File

@@ -38,6 +38,4 @@ After building, the output .JAR file should be in the output JAR file should be
<a href="https://anuke.itch.io/mindustry"><img src="https://i.imgur.com/sk26hTV.png" width="auto" height="75"></a> <a href="https://anuke.itch.io/mindustry"><img src="https://i.imgur.com/sk26hTV.png" width="auto" height="75"></a>
<a href="https://anuke.itch.io/mindustry"><img src="https://i.imgur.com/ZSPVQpn.gif" width="auto" height="75"></a>
<a href="https://play.google.com/store/apps/details?id=io.anuke.mindustry&hl=en"><img src="https://i.imgur.com/8dF6l81.png" width="auto" height="75"></a> <a href="https://play.google.com/store/apps/details?id=io.anuke.mindustry&hl=en"><img src="https://i.imgur.com/8dF6l81.png" width="auto" height="75"></a>

View File

@@ -20,7 +20,7 @@ import io.anuke.kryonet.DefaultThreadImpl;
import io.anuke.kryonet.KryoClient; import io.anuke.kryonet.KryoClient;
import io.anuke.kryonet.KryoServer; import io.anuke.kryonet.KryoServer;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.TextField;

View File

@@ -27,7 +27,7 @@ allprojects {
gdxVersion = '1.9.8' gdxVersion = '1.9.8'
roboVMVersion = '2.3.0' roboVMVersion = '2.3.0'
aiVersion = '1.8.1' aiVersion = '1.8.1'
uCoreVersion = '55f0d90' uCoreVersion = 'd5af97f93813d8767423521b1fcc5a5e0f7241d9'
getVersionString = { getVersionString = {
String buildVersion = getBuildVersion() String buildVersion = getBuildVersion()
@@ -85,7 +85,7 @@ project(":desktop") {
compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion" compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion"
compile 'com.github.MinnDevelopment:java-discord-rpc:v1.3.2' compile 'com.github.MinnDevelopment:java-discord-rpc:v2.0.0'
} }
} }
@@ -144,8 +144,7 @@ project(":ios") {
} }
robovm { robovm {
iosSignIdentity = "a"
iosProvisioningProfile = ""
} }
} }

View File

@@ -1,4 +1,4 @@
text.about=Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\nOriginally an entry in the [orange]GDL[] Metal Monstrosity Jam.\n\nCredits:\n- SFX made with [YELLOW]bfxr[]\n- Music made by [GREEN]Roccow[]\n\nSpecial thanks to:\n- [coral]MitchellFJN[]: extensive playtesting and feedback\n- [sky]Luxray5474[]: wiki work, code contributions\n- [lime]Epowerj[]: code build system, icon\n- All the beta testers on itch.io and Google Play\n text.about=Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\nOriginally an entry in the [orange]GDL[] Metal Monstrosity Jam.\n\nCredits:\n- SFX made with [YELLOW]bfxr[]\n- Music made by [GREEN]a beat a day[]\n\nSpecial thanks to:\n- [coral]MitchellFJN[]: extensive playtesting and feedback\n- [sky]Luxray5474[]: wiki work, code contributions\n- [lime]Epowerj[]: code build system, icon\n- All the beta testers on itch.io and Google Play\n
text.credits=Credits text.credits=Credits
text.discord=Join the mindustry discord! text.discord=Join the mindustry discord!
text.changes=[SCARLET]Attention!\n[]Some important game mechanics have been changed.\n\n- [accent]Teleporters[] now use power.\n- [accent]Smelteries[] and [accent]crucibles[] now have a maximum item capacity.\n- [accent]Crucibles[] now require coal as fuel. text.changes=[SCARLET]Attention!\n[]Some important game mechanics have been changed.\n\n- [accent]Teleporters[] now use power.\n- [accent]Smelteries[] and [accent]crucibles[] now have a maximum item capacity.\n- [accent]Crucibles[] now require coal as fuel.
@@ -70,6 +70,8 @@ text.server.bans=Bans
text.server.bans.none=No banned players found! text.server.bans.none=No banned players found!
text.server.admins=Admins text.server.admins=Admins
text.server.admins.none=No admins found! text.server.admins.none=No admins found!
text.server.rollback=Rollback
text.server.rollback.numberfield=Rollback Amount:
text.server.add=Add Server text.server.add=Add Server
text.server.delete=Are you sure you want to delete this server? text.server.delete=Are you sure you want to delete this server?
text.server.hostname=Host: {0} text.server.hostname=Host: {0}
@@ -105,7 +107,7 @@ text.savefail=Failed to save game!
text.save.delete.confirm=Are you sure you want to delete this save? text.save.delete.confirm=Are you sure you want to delete this save?
text.save.delete=Delete text.save.delete=Delete
text.save.export=Export Save text.save.export=Export Save
text.save.import.invalid=[orange]This save is invalid! text.save.import.invalid=[orange]This save is invalid!\n\nNote that[scarlet]importing saves with custom maps[orange]\nfrom other devices does not work!
text.save.import.fail=[crimson]Failed to import save: [orange]{0} text.save.import.fail=[crimson]Failed to import save: [orange]{0}
text.save.export.fail=[crimson]Failed to export save: [orange]{0} text.save.export.fail=[crimson]Failed to export save: [orange]{0}
text.save.import=Import Save text.save.import=Import Save
@@ -209,6 +211,8 @@ placemode.touch.name=touch
placemode.cursor.name=cursor placemode.cursor.name=cursor
text.blocks.extrainfo=[accent]extra block info: text.blocks.extrainfo=[accent]extra block info:
text.blocks.blockinfo=Block Info text.blocks.blockinfo=Block Info
text.blocks.editlogs=Edit Logs
text.block.editlogsnotfound=[red]There are no edit logs for this location.
text.blocks.powercapacity=Power Capacity text.blocks.powercapacity=Power Capacity
text.blocks.powershot=Power/shot text.blocks.powershot=Power/shot
text.blocks.powersecond=Power/second text.blocks.powersecond=Power/second

View File

@@ -1,44 +1,44 @@
text.about = Creado por [ROYAL] Anuken [] - [SKY] anukendev@gmail.com [] Originalmente una entrada en el [naranja] GDL [] Metal Monstrosity Jam. Créditos: - SFX hecho con [AMARILLO] bfxr [] - Música hecha por [VERDE] RoccoW [] / encontrado en [lime] FreeMusicArchive.org [] Agradecimientos especiales a: - [coral] MitchellFJN []: extensa prueba de juego y comentarios - [cielo] Luxray5474 []: trabajo wiki, contribuciones de código - [lime] Epowerj []: sistema de compilación de código, icono - Todos los probadores beta en itch.io y Google Play\n text.about = Creado por [ROYAL]Anuken [] - [SKY] anukendev@gmail.com [] Originalmente una entrada en el [naranja] GDL [] Metal Monstrosity Jam. Créditos: - SFX hecho con [AMARILLO] bfxr [] - Música hecha por [VERDE] RoccoW [] / encontrado en [lime] FreeMusicArchive.org [] Agradecimientos especiales a: - [coral] MitchellFJN []: extensa prueba de juego y comentarios - [cielo] Luxray5474 []: trabajo wiki, contribuciones de código - [lime] Epowerj []: sistema de compilación de código, icono - Todos los probadores beta en itch.io y Google Play\n
text.credits = Créditos text.credits = Créditos
text.discord = ¡Únete al Discord de Mindustry! text.discord = ¡Únete al Discord de Mindustry!
text.changes = [SCARLET] ¡Atención! [] Algunas mecánicas importantes del juego han sido cambiadas. - [acento] Los teleportadores [] ahora usan energía. - [acento] Los crisoles de Smelteries [] y [acento] [] ahora tienen una capacidad máxima de artículo. - [acento] Crucibles [] ahora requieren carbón como combustible. text.changes = [SCARLET] ¡Atención! [] Algunas mecánicas importantes del juego han sido cambiadas. - [acento] Los teletransportadores [] ahora usan energía. - [acento]Los talleres de fundición[] y [acento]los crisoles [] ahora tienen una capacidad máxima de artículos. - [acento] Los crisoles[] ahora requieren carbón como combustible.
text.link.discord.description = La sala oficial del discord de Mindustry text.link.discord.description = La sala oficial del discord de Mindustry
text.link.github.description = Código fuente del juego text.link.github.description = Código fuente del juego
text.link.dev-builds.description = Desarrollos de desarrollo inestables text.link.dev-builds.description = Estados en desarrollo inestables
text.link.trello.description = Tablero trello oficial para las características planificadas text.link.trello.description = Tablero trello oficial para las características planificadas
text.link.itch.io.description = itch.io página con descargas de PC y versión web text.link.itch.io.description = itch.io és la página con descargas para PC y la versión web
text.link.google-play.description = Listado en la tienda de Google Play text.link.google-play.description = Listado en la tienda de Google Play
text.link.wiki.description = Wiki oficial de Mindustry text.link.wiki.description = Wiki oficial de Mindustry
text.linkfail = Error al abrir el enlace! La URL ha sido copiada a su portapapeles text.linkfail = ¡Error al abrir el enlace!\nLa URL ha sido copiada a su portapapeles
text.editor.web = ¡La versión web no es compatible con el editor! Descargue el juego para usarlo. text.editor.web = ¡La versión web no es compatible con el editor!\nDescargue el juego para usarlo.
text.multiplayer.web = ¡Esta versión del juego no admite multijugador! Para jugar al modo multijugador desde su navegador, use el enlace \"versión de varios jugadores\" en la página itch.io. text.multiplayer.web = ¡Esta versión del juego no admite multijugador!\nPara jugar al modo multijugador desde su navegador, use el enlace \"versión de varios jugadores\" en la página itch.io.
text.gameover = El núcleo fue destruido. text.gameover = El núcleo fue destruido.
text.highscore = [YELLOW] ¡Nueva puntuación! text.highscore = [YELLOW]¡Nueva mejor puntuación!
text.lasted = Duró hasta la ronda text.lasted = Duró hasta la ronda
text.level.highscore = Puntuación\nmàs alta: [accent]{0} text.level.highscore = Puntuación màs alta: [accent]
text.level.delete.title = Confirmar Eliminación text.level.delete.title = Confirmar Eliminación
text.level.delete = ¿Seguro que quieres eliminar el mapa \"[ORANGE] \"? text.level.delete = ¿Seguro que quieres eliminar el mapa \"[ORANGE] \" {0}?
text.level.select = Selección de nivel text.level.select = Selección de nivel
text.level.mode = Modo de juego: text.level.mode = Modo de juego:
text.savegame = Guardar Juego text.savegame = Guardar Partida
text.loadgame = Cargar Juego text.loadgame = Cargar Partida
text.joingame = Unirse a\nun juego text.joingame = Unirse a una Partida
text.newgame = Nuevo Juego text.newgame = Nueva Partida
text.quit = Salir text.quit = Salir
text.about.button = Acerca de text.about.button = Acerca de
text.name = Nombre text.name = Nombre
text.public = Público text.public = Público
text.players = {0} Jugadores en línea text.players = {0} Jugadores en línea
text.server.player.host = ANFITRIÓN text.server.player.host = {0} ANFITRIÓN
text.players.single = {0} jugador en línea text.players.single = {0} jugador en línea
text.server.mismatch = Error de paquete: posible desajuste de la versión cliente / servidor. ¡Asegúrate de que tú y el anfitrión tengas la última versión de Mindustry! text.server.mismatch = Error de paquete: posible desajuste de la versión cliente / servidor.\n¡Asegúrate de que tú y el anfitrión tengáis la última versión de Mindustry!
text.server.closing = [accent] Cerrando servidor ... text.server.closing = [accent] Cerrando servidor ...
text.server.kicked.kick = ¡Has sido expulsado del servidor! text.server.kicked.kick = ¡Has sido expulsado del servidor!
text.server.kicked.invalidPassword = ¡Contraseña inválida! text.server.kicked.invalidPassword = ¡Contraseña inválida!
text.server.kicked.clientOutdated = Cliente desactualizado ¡Actualiza tu juego! text.server.kicked.clientOutdated = Cliente desactualizado ¡Actualiza tu juego!
text.server.kicked.serverOutdated = Servidor desactualizado ¡Pregúntale al anfitrión para actualizar! text.server.kicked.serverOutdated = Servidor desactualizado ¡Pidele actualizar al anfitrión!
text.server.kicked.banned = Estás prohibido en este servidor. text.server.kicked.banned = Tu entrada está prohibida en este servidor.
text.server.kicked.recentKick = Te han kickeado recientemente. Espera antes de conectarte de nuevo. text.server.kicked.recentKick = Has sido echado recientemente.\nEspera antes de conectarte de nuevo.
text.server.connected = se ha unido. text.server.connected = se ha unido.
text.server.disconnected = se ha desconectado text.server.disconnected = se ha desconectado
text.nohost = ¡No se puede alojar el servidor en un mapa personalizado! text.nohost = ¡No se puede alojar el servidor en un mapa personalizado!
@@ -71,7 +71,7 @@ text.server.admins = Admins
text.server.admins.none = ¡No se encontraron administradores! text.server.admins.none = ¡No se encontraron administradores!
text.server.add = Agregar servidor text.server.add = Agregar servidor
text.server.delete = ¿Seguro que quieres eliminar este servidor? text.server.delete = ¿Seguro que quieres eliminar este servidor?
text.server.hostname = ANFITRIÓN text.server.hostname = Anfitrión: {0}
text.server.edit = Editar servidor text.server.edit = Editar servidor
text.server.outdated = [crimson] ¡Servidor obsoleto! [] text.server.outdated = [crimson] ¡Servidor obsoleto! []
text.server.outdated.client = [carmesí] Cliente desactualizado! [] text.server.outdated.client = [carmesí] Cliente desactualizado! []
@@ -105,23 +105,23 @@ text.save.delete.confirm = ¿Estás seguro de que deseas eliminar este guardado?
text.save.delete = Borrar text.save.delete = Borrar
text.save.export = Exportar guardado text.save.export = Exportar guardado
text.save.import.invalid = [orange] ¡Este guardado es inválido! text.save.import.invalid = [orange] ¡Este guardado es inválido!
text.save.import.fail = [crimson] Fallo al importar guardado: [orange] text.save.import.fail = [crimson] Fallo al importar guardado: [orange] {0}
text.save.export.fail = [crimson] Fallo al exportar guardado: [orange] text.save.export.fail = [crimson] Fallo al exportar guardado: [orange] {0}
text.save.import = Importar Guardado text.save.import = Importar Guardado
text.save.newslot = Nombre del guardado: text.save.newslot = Nombre del guardado:
text.save.rename = Renombrar text.save.rename = Renombrar
text.save.rename.text = Nuevo nombre text.save.rename.text = Nuevo nombre
text.selectslot = Seleccionar una guardado text.selectslot = Seleccionar una guardado
text.slot = [accent] Casilla text.slot = [accent] Casilla {0}
text.save.corrupted = [orange] ¡Arhivo de guardado corrupto o inválido! text.save.corrupted = [orange] ¡Arhivo de guardado corrupto o inválido!
text.empty = <Vacío> text.empty = <Vacío>
text.on = Encendido text.on = Encendido
text.off = Apagado text.off = Apagado
text.save.autosave = Guardado automático: text.save.autosave = Guardado automático: {0}
text.save.map = Mapa: text.save.map = Mapa: {0}
text.save.wave = Horda: text.save.wave = Horda: {0}
text.save.difficulty = Dificultad text.save.difficulty = Dificultad: {0}
text.save.date = Guardado por última vez: text.save.date = Guardado por última vez: {0}
text.confirm = Confirmar text.confirm = Confirmar
text.delete = Eliiminar text.delete = Eliiminar
text.ok = OK text.ok = OK
@@ -138,28 +138,28 @@ text.changelog.error = [escarlata] ¡Error al obtener el registro de cambios! Co
text.changelog.current = [amarillo] [[Versión actual] text.changelog.current = [amarillo] [[Versión actual]
text.changelog.latest = [naranja] [[Última versión] text.changelog.latest = [naranja] [[Última versión]
text.loading = [accent] Cargando... text.loading = [accent] Cargando...
text.wave = [orange] Horda text.wave = [orange] Horda {0}
text.wave.waiting = Horda en text.wave.waiting = Horda en {0}
text.waiting = Esperando... text.waiting = Esperando...
text.enemies = Enemigos text.enemies = {0} Enemigos
text.enemies.single = Enemigo text.enemies.single = {0} Enemigo
text.loadimage = Cargar imagen text.loadimage = Cargar imagen
text.saveimage = Guardar imagen text.saveimage = Guardar imagen
text.oregen = Generación\nde mineral text.oregen = Generación de mineral {0}
text.editor.badsize = [orange]¡Dimensiones de imagen inválidas![]\nDimensiones de mapa válidas: text.editor.badsize = [orange]¡Dimensiones de imagen inválidas![]\nDimensiones de mapa válidas: {0}
text.editor.errorimageload = Error al cargar el archivo de imagen: [orange] text.editor.errorimageload = Error al cargar el archivo de imagen: [orange] {0}
text.editor.errorimagesave = Error al guardar el archivo de imagen: [orange] text.editor.errorimagesave = Error al guardar el archivo de imagen: [orange] {0}
text.editor.generate = Generar text.editor.generate = Generar
text.editor.resize = Cambiar\ntamaño text.editor.resize = Cambiar tamaño
text.editor.loadmap = Cargar\nmapa text.editor.loadmap = Cargar mapa
text.editor.savemap = Guardar\nmapa text.editor.savemap = Guardar mapa
text.editor.loadimage = Cargar\nimagen text.editor.loadimage = Cargar imagen
text.editor.saveimage = Guardar\nimagen text.editor.saveimage = Guardar imagen
text.editor.unsaved = [scarlet] ¡Tienes cambios sin guardar! [] ¿Estás seguro de que quieres salir? text.editor.unsaved = [scarlet] ¡Tienes cambios sin guardar! [] ¿Estás seguro de que quieres salir?
text.editor.brushsize = Tamaño\ndel pincel: text.editor.brushsize = Tamaño del pincel: {0}
text.editor.noplayerspawn = ¡Este mapa no tiene punto de aparición del jugador! text.editor.noplayerspawn = ¡Este mapa no tiene punto de aparición del jugador!
text.editor.manyplayerspawns = ¡Los mapas no pueden tener más de un punto de spawn de jugador! text.editor.manyplayerspawns = ¡Los mapas no pueden tener más de un punto de spawn de jugador!
text.editor.manyenemyspawns = ¡No puede tener más de puntos de aparición enemiga! text.editor.manyenemyspawns = {0 }¡No puede tener más de puntos de aparición enemiga!
text.editor.resizemap = Cambiar el tamaño del mapa text.editor.resizemap = Cambiar el tamaño del mapa
text.editor.resizebig = [escarlata] ¡Advertencia! [] Los mapas de más de 256 unidades pueden ser inestables. text.editor.resizebig = [escarlata] ¡Advertencia! [] Los mapas de más de 256 unidades pueden ser inestables.
text.editor.mapname = Nombre del mapa text.editor.mapname = Nombre del mapa
@@ -362,7 +362,7 @@ mode.waves.name = Hordas
mode.waves.description = El modo normal. Recursos limitados y las hordas vendrán automáticamente mode.waves.description = El modo normal. Recursos limitados y las hordas vendrán automáticamente
mode.sandbox.name = Sandbox mode.sandbox.name = Sandbox
mode.sandbox.description = Recursos infinitos y sin temporizador para las olas. mode.sandbox.description = Recursos infinitos y sin temporizador para las olas.
mode.freebuild.name = Construcción\nlibre mode.freebuild.name = Construcción libre
mode.freebuild.description = Recursos limitados y sin tiempo definido para las hordas mode.freebuild.description = Recursos limitados y sin tiempo definido para las hordas
upgrade.standard.name = Estandar upgrade.standard.name = Estandar
upgrade.standard.description = El mech estándar. upgrade.standard.description = El mech estándar.
@@ -539,14 +539,14 @@ block.sniperturret.fulldescription = Torreta de largo alcance avanzada. Utiliza
block.mortarturret.name = Torreta antiaérea block.mortarturret.name = Torreta antiaérea
block.mortarturret.fulldescription = Torreta avanzada de baja salpicadura de daños por salpicadura. Utiliza carbón para munición. Dispara un aluvión de balas que explotan en metralla. Útil para grandes multitudes de enemigos. block.mortarturret.fulldescription = Torreta avanzada de baja salpicadura de daños por salpicadura. Utiliza carbón para munición. Dispara un aluvión de balas que explotan en metralla. Útil para grandes multitudes de enemigos.
block.laserturret.name = Torreta láser block.laserturret.name = Torreta láser
block.laserturret.fulldescription = Torreta de un solo objetivo avanzado. Utiliza el poder Buena torrecilla de medio alcance. Solo objetivo único. Nunca falla block.laserturret.fulldescription = Torreta de un solo objetivo avanzado. Utiliza el energia. Buena torre de medio alcance. Objetivo único. Nunca falla
block.waveturret.name = Torreta tesla block.waveturret.name = Torreta tesla
block.waveturret.fulldescription = Torreta multi-objetivo avanzada. Utiliza el poder Rango medio. Nunca falla. Promedio a bajo daño, pero puede golpear a varios enemigos simultáneamente con la iluminación de la cadena. block.waveturret.fulldescription = Torreta multi-objetivo avanzada. Utiliza el poder Rango medio. Nunca falla. De Medio a bajo daño, pero puede golpear a varios enemigos simultáneamente con rayos en cadena.
block.plasmaturret.name = Torreta de plasma block.plasmaturret.name = Torreta de plasma
block.plasmaturret.fulldescription = Versión altamente avanzada de la torreta de fuego. Utiliza carbón como munición. Daño muy alto, rango bajo a medio. block.plasmaturret.fulldescription = Versión altamente avanzada de la torreta de fuego. Utiliza carbón como munición. Daño muy alto, rango bajo a medio.
block.chainturret.name = Torreta de cadena block.chainturret.name = Torreta de cadena
block.chainturret.fulldescription = La última torreta de fuego rápido. Usa uranio como munición. Dispara babosas grandes a una alta tasa de fuego. Rango medio. Se extiende por múltiples mosaicos. Extremadamente duro. block.chainturret.fulldescription = La torreta de fuego rápido suprema. Usa uranio como munición. Dispara babosas grandes a una alta cadencia. Rango medio. Se extiende por múltiples bloques. Extremadamente duradero.
block.titancannon.name = Cañón titán block.titancannon.name = Cañón titán
block.titancannon.fulldescription = La torreta suprema de largo alcance. Usa uranio como munición. Dispara grandes proyectiles de daño de área a una velocidad de fuego media. De largo alcance. Se extiende por múltiples bloques. Extremadamente durable. block.titancannon.fulldescription = La torreta de largo alcance suprema. Usa uranio como munición. Dispara grandes proyectiles con daño de área a una cadencia media. De largo alcance. Se extiende por múltiples bloques. Extremadamente duradero.
block.playerspawn.name = Punto de aparición del jugador block.playerspawn.name = Punto de aparición del jugador
block.enemyspawn.name = Generador de enemigos block.enemyspawn.name = Generador de enemigos

View File

@@ -1,4 +1,4 @@
text.about = 만든이 : [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n이 게임은 [orange]GDL[] Metal Monstrosity Jam 을 사용했습니다.\n\n크레딧\n- [YELLOW]bfxr[] 가 SFX 를 만듬\n- [GREEN]a drop a day[] 가 음악을 만듬\n\n특별히 감사한 분들\n- [coral]MitchellFJN[]: 테스트하고 피드백을 주신 분\n- [sky]Luxray5474[]: wiki 를 만들고 코드에 기여하신 분\n- [lime]Epowerj[]: 코드를 만들고 아이콘을 제작하신 분\n- itch.io 그리고 Google Play 에서의 모든 베타 테스터 분들\n text.about = 만든이 : [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n이 게임은 [orange]GDL[] Metal Monstrosity Jam 을 사용했습니다.\n\n크레딧\n- [YELLOW]bfxr[] 가 SFX 를 만듬\n- [GREEN]Roccow[] 가 음악을 만듬\n\n특별히 감사한 분들\n- [coral]MitchellFJN[]: 테스트하고 피드백을 주신 분\n- [sky]Luxray5474[]: wiki 를 만들고 코드에 기여하신 분\n- [lime]Epowerj[]: 코드를 만들고 아이콘을 제작하신 분\n- itch.io 그리고 Google Play 에서의 모든 베타 테스터 분들\n
text.credits = 크레딧 text.credits = 크레딧
text.discord = Mindustry 디스코드에 참여하세요! text.discord = Mindustry 디스코드에 참여하세요!
text.changes = [SCARLET]주의!\n[]몇몇 중요한 게임 메커니즘이 변경되었습니다.\n\n- [accent]텔레포터[]는 이제 전력을 사용합니다.\n- [accent]제련소[]와 [accent]도가니[] 는 이제 최대 자원저장 공간을 가집니다.\n- [accent]도가니[] 는 이제 석탄 연료를 필요로 합니다. text.changes = [SCARLET]주의!\n[]몇몇 중요한 게임 메커니즘이 변경되었습니다.\n\n- [accent]텔레포터[]는 이제 전력을 사용합니다.\n- [accent]제련소[]와 [accent]도가니[] 는 이제 최대 자원저장 공간을 가집니다.\n- [accent]도가니[] 는 이제 석탄 연료를 필요로 합니다.
@@ -36,7 +36,7 @@ text.server.closing = [accent]서버 닫는중...
text.server.kicked.kick = 당신은 서버에서 강제 퇴장 되었습니다. text.server.kicked.kick = 당신은 서버에서 강제 퇴장 되었습니다.
text.server.kicked.invalidPassword = 잘못된 비밀번호 입니다! text.server.kicked.invalidPassword = 잘못된 비밀번호 입니다!
text.server.kicked.clientOutdated = 현재 플레이중인 게임 버전이 낮습니다!\n게임을 업데이트 해 주세요. text.server.kicked.clientOutdated = 현재 플레이중인 게임 버전이 낮습니다!\n게임을 업데이트 해 주세요.
text.server.kicked.serverOutdated = 낮은 버전의 서버입니다! 서버장에게 업데이트를 요청하세요! text.server.kicked.serverOutdated = 이 서버는 현재 클라이언트보다 낮은 버전의 서버입니다!\n서버장에게 업데이트를 요청하세요!
text.server.kicked.banned = 당신은 이 서버에서 차단되었습니다. text.server.kicked.banned = 당신은 이 서버에서 차단되었습니다.
text.server.kicked.recentKick = 최근에 강제 퇴장되었습니다.\n잠시 후에 다시 입장 해 주세요. text.server.kicked.recentKick = 최근에 강제 퇴장되었습니다.\n잠시 후에 다시 입장 해 주세요.
text.server.connected = {0} 님이 서버에 입장했습니다. text.server.connected = {0} 님이 서버에 입장했습니다.
@@ -54,29 +54,29 @@ text.hosts.none = [lightgray]LAN 게임이 없습니다!
text.host.invalid = [scarlet]호스트에 연결할 수 없습니다! text.host.invalid = [scarlet]호스트에 연결할 수 없습니다!
text.server.friendlyfire = 팀킬 허용 text.server.friendlyfire = 팀킬 허용
text.trace = 플레이어 추적 text.trace = 플레이어 추적
text.trace.playername = 플레이어 이름 : [accent] {0} text.trace.playername = 플레이어 이름 : [accent]{0}
text.trace.ip = IP : [accent]{0} text.trace.ip = IP : [accent]{0}
text.trace.id = 고유 ID : [accent] {0} text.trace.id = 고유 ID : [accent]{0}
text.trace.android = Android 클라이언트 : [accent] {0} text.trace.android = Android 클라이언트 : [accent]{0}
text.trace.modclient = 수정된 클라이언트 : [accent] {0} text.trace.modclient = 수정된 클라이언트 : [accent]{0}
text.trace.totalblocksbroken = 총 파괴한 블록 수 : [accent] {0} text.trace.totalblocksbroken = 총 파괴한 블록 수 : [accent]{0}
text.trace.structureblocksbroken = 총 구조 블럭 파괴수 : [accent] {0} text.trace.structureblocksbroken = 총 구조 블럭 파괴수 : [accent]{0}
text.trace.lastblockbroken = 마지막으로 파괴한 블록 : [accent] {0} text.trace.lastblockbroken = 마지막으로 파괴한 블록 : [accent]{0}
text.trace.totalblocksplaced = 총 설치한 블록 수 : [accent] {0} text.trace.totalblocksplaced = 총 설치한 블록 수 : [accent]{0}
text.trace.lastblockplaced = 마지막으로 설치한 블록 : [accent] {0} text.trace.lastblockplaced = 마지막으로 설치한 블록 : [accent]{0}
text.invalidid = 잘못된 클라이언트 ID 입니다! 공식 Mindustry 으로 버그 보고서를 제출 해 주세요. text.invalidid = 잘못된 클라이언트 ID 입니다! 공식 Mindustry 으로 버그 보고서를 제출 해 주세요.
text.server.bans = 차단된 플레이어 text.server.bans = 차단된 유저
text.server.bans.none = 차단된 플레이어가 없습니다. text.server.bans.none = 차단된 플레이어가 없습니다.
text.server.admins = 관리자 text.server.admins = 관리자
text.server.admins.none = 관리자가 없습니다. text.server.admins.none = 관리자가 없습니다.
text.server.add = 서버 추가 text.server.add = 서버 추가
text.server.delete = 이 서버를 삭제 하시겠습니까? text.server.delete = 이 서버를 삭제 하시겠습니까?
text.server.hostname = 호스트: text.server.hostname = 호스트: {0}
text.server.edit = 서버 수정 text.server.edit = 서버 수정
text.server.outdated = [crimson]서버 버전이 낮습니다![] text.server.outdated = [crimson]서버 버전이 낮습니다![]
text.server.outdated.client = [Crimson]클라이언트 버전이 낮습니다![] text.server.outdated.client = [Crimson]클라이언트 버전이 낮습니다![]
text.server.version = [lightgray] 버전 : {0} text.server.version = [lightgray] 버전 : {0}
text.server.custombuild = [노란색]수정된 빌드 text.server.custombuild = [yellow]custom server
text.confirmban = 이 플레이어를 차단하시겠습니까? text.confirmban = 이 플레이어를 차단하시겠습니까?
text.confirmunban = 이 플레이어를 차단 해제 하시겠습니까? text.confirmunban = 이 플레이어를 차단 해제 하시겠습니까?
text.confirmadmin = 이 플레이어를 관리자로 설정 하시겠습니까? text.confirmadmin = 이 플레이어를 관리자로 설정 하시겠습니까?
@@ -124,7 +124,7 @@ text.save.difficulty = 난이도 : {0}
text.save.date = 마지막 저장 날짜 : {0} text.save.date = 마지막 저장 날짜 : {0}
text.confirm = 확인 text.confirm = 확인
text.delete = 삭제 text.delete = 삭제
text.ok = OK text.ok = 확인
text.open = 열기 text.open = 열기
text.cancel = 취소 text.cancel = 취소
text.openlink = 링크 열기 text.openlink = 링크 열기
@@ -140,7 +140,7 @@ text.changelog.current = [yellow][[현재 버전]
text.changelog.latest = [orange][[최신 버전] text.changelog.latest = [orange][[최신 버전]
text.loading = [accent]로딩중 ... text.loading = [accent]로딩중 ...
text.wave = [orange]{0} 단계 text.wave = [orange]{0} 단계
text.wave.waiting = 다음 레벨 {0} 시작 대기중 text.wave.waiting = 다음 단계까지 {0} 초 남음
text.waiting = 대기 중... text.waiting = 대기 중...
text.enemies = 남은 잡몹 수 : {0} text.enemies = 남은 잡몹 수 : {0}
text.enemies.single = {0} 마리 남음 text.enemies.single = {0} 마리 남음

View File

@@ -205,7 +205,7 @@ setting.difficulty.easy = łatwy
setting.difficulty.normal = normalny setting.difficulty.normal = normalny
setting.difficulty.hard = trudny setting.difficulty.hard = trudny
setting.difficulty.insane = szalony setting.difficulty.insane = szalony
setting.difficulty.purge = Usuń setting.difficulty.purge = Czystka
setting.difficulty.name = Poziom trudności setting.difficulty.name = Poziom trudności
setting.screenshake.name = Trzęsienie się ekranu setting.screenshake.name = Trzęsienie się ekranu
setting.smoothcam.name = Płynna kamera setting.smoothcam.name = Płynna kamera
@@ -373,8 +373,8 @@ block.titaniumwall-large.name = duża tytanowa ściana
block.titaniumwall-large.fulldescription = Silny blok obronny. Rozpiętość wielu płytek. block.titaniumwall-large.fulldescription = Silny blok obronny. Rozpiętość wielu płytek.
block.duriumwall-large.name = duża ściana z dirium block.duriumwall-large.name = duża ściana z dirium
block.duriumwall-large.fulldescription = Bardzo silny blok obronny. Rozpiętość wielu płytek. block.duriumwall-large.fulldescription = Bardzo silny blok obronny. Rozpiętość wielu płytek.
block.titaniumshieldwall.name = Ściana ekranowana block.titaniumshieldwall.name = Ściana z polem obronnym
block.titaniumshieldwall.fulldescription = Silny blok obronny z dodatkową wbudowaną tarczą. Wymaga zasilania. Używa energii do pochłaniania pocisków wroga. Zaleca się stosowanie wzmacniaczy energii w celu dostarczenia jej ściany. block.titaniumshieldwall.fulldescription = Silny blok obronny z dodatkową wbudowaną tarczą. Wymaga zasilania. Używa energii do pochłaniania pocisków wroga. W celu dostarczenia zasilania zaleca się stosowanie wzmacniaczy energii.
block.repairturret.name = Wieża naprawcza block.repairturret.name = Wieża naprawcza
block.repairturret.fulldescription = Naprawia pobliskie uszkodzone bloki w niedużej prędkości. Wykorzystuje niewielkie ilości energii. block.repairturret.fulldescription = Naprawia pobliskie uszkodzone bloki w niedużej prędkości. Wykorzystuje niewielkie ilości energii.
block.megarepairturret.name = Wieża naprawcza II block.megarepairturret.name = Wieża naprawcza II

View File

@@ -1,5 +1,17 @@
text.about = Создатель [ROYAL] Anuken. [] \nИзначально игра была создана для участия в [orange] GDL [] MM Jam. \n\nАвторы: \n- Звуковые эффекты, сделаны с помощью [YELLOW] bfxr [] \n- Музыка, создана [GREEN] RoccoW [] / найденная на [lime] FreeMusicArchive.org [] \n\nОсобая благодарность: \n- [coral] MitchellFJN []: в тестировании и отзывах \n- [sky] Luxray5474 []: работа в вики, помощь в разработке \n- Все бета-тестеры на itch.io и Google Play\n\nИгра переведена полностью на русский язык [GREEN]krocotavus[] и [GREEN]lexa1549[]\n text.about = Создатель [ROYAL] Anuken. [] \nИзначально игра была создана для участия в [orange] GDL [] MM Jam. \n\nАвторы: \n- Звуковые эффекты, сделаны с помощью [YELLOW] bfxr [] \n- Музыка, создана [GREEN] RoccoW [] / найденная на [lime] FreeMusicArchive.org [] \n\nОсобая благодарность: \n- [coral] MitchellFJN []: в тестировании и отзывах \n- [sky] Luxray5474 []: работа в вики, помощь в разработке \n- Все бета-тестеры на itch.io и Google Play\n\nИгра переведена полностью на русский язык [GREEN]krocotavus[] и [GREEN]lexa1549. Дополнил перевод [GREEN]Prosta4ok_ua[]\n
text.credits = Авторы
text.discord = Присоединяйтесь к нашему Discord чату! text.discord = Присоединяйтесь к нашему Discord чату!
text.changes=[SCARLET] Внимание!\n[]Изменена некоторая важная игровая механика.\n\n-[accent]Телепортеры[]теперь используют силу.\n-[accent]Печи[]и[accent]тигли[]теперь имеют максимальная емкость элемента.\n-[accent]Тигли[]теперь требует угля в качестве топлива.
text.link.discord.description = официальный discord-сервер Mindustry
text.link.github.description = Исходный код игры
text.link.dev-builds.description = Нестабильные разработки
text.link.trello.description = Официальная доска trello для запланированных функций
text.link.itch.io.description = itch.io страница с загрузкой ПК и веб-версией
text.link.google-play.description = Google Play список магазинов
text.link.wiki.description = официальная вики Mindustry
text.linkfail = Не удалось открыть ссылку!\nURL-адрес был скопирован в буфер обмена.
text.editor.web = Веб-версия не поддерживает редактор!\nЗагрузите игру, чтобы использовать ее.
text.multiplayer.web = Эта версия игры не поддерживает многопользовательскую игру! \n Чтобы играть в мультиплеер из своего браузера, используйте ссылку «Многопользовательская веб-версия» на странице itch.io.
text.gameover = Ядро было уничтожено. text.gameover = Ядро было уничтожено.
text.highscore = [YELLOW]Новый рекорд! text.highscore = [YELLOW]Новый рекорд!
text.lasted = Вы продержались до волны text.lasted = Вы продержались до волны
@@ -11,19 +23,28 @@ text.level.mode = Режим игры:
text.savegame = Сохранить игру text.savegame = Сохранить игру
text.loadgame = Загрузить игру text.loadgame = Загрузить игру
text.joingame = Присоединиться text.joingame = Присоединиться
text.newgame= Новая игра
text.quit = Выход text.quit = Выход
text.about.button = Об игре text.about.button = Об игре
text.name = Название: text.name = Название:
text.public = Общие text.public = Общие
text.players = Игроков на сервере: {0} text.players = Игроков на сервере: {0}
text.server.player.host={0} (хост)
text.players.single = {0} игрок на сервере text.players.single = {0} игрок на сервере
text.server.mismatch = Ошибка пакета: возможное несоответствие версии клиента / сервера. Убедитесь, что у вас и у создателя сервера установлена ​​последняя версия Mindustry! text.server.mismatch = Ошибка пакета: возможное несоответствие версии клиента / сервера. Убедитесь, что у вас и у создателя сервера установлена ​​последняя версия Mindustry!
.server.closing = [accent]Закрытие сервера... text.server.closing = [accent]Закрытие сервера...
text.server.kicked.kick = Вас выгнали с сервера! text.server.kicked.kick = Вас выгнали с сервера!
text.server.kicked.fastShoot = Вы стреляете слишком быстро.
text.server.kicked.invalidPassword = Неверный пароль. text.server.kicked.invalidPassword = Неверный пароль.
text.server.kicked.clientOutdated = Устаревший клиент! Обновите игру!
text.server.kicked.serverOutdated = Устаревший сервер! Попросите хост обновить!
text.server.kicked.banned = Вы заблокированы на этом сервере.
text.server.kicked.recentKick=Вы недавно были кикнуты.\n Подождите немного перед следующим подключением
text.server.connected = {0} присоединился text.server.connected = {0} присоединился
text.server.disconnected = {0} отключился. text.server.disconnected = {0} отключился.
text.nohost = Не удается запустить сервер на пользовательской карте! text.nohost = Не удается запустить сервер на пользовательской карте!
text.host.info=The [accent]host[] button hosts a server on ports [scarlet]6567[] and [scarlet]6568.[]\nAnybody on the same [LIGHT_GRAY]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[LIGHT_GRAY]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings.
text.join.info=Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[LIGHT_GRAY]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP.
text.hostserver = Запустить сервер text.hostserver = Запустить сервер
text.host = Сервер text.host = Сервер
text.hosting = [accent]Открытие сервера... text.hosting = [accent]Открытие сервера...
@@ -32,18 +53,46 @@ text.hosts.discovering = Поиск локальных игр
text.server.refreshing = Обновление сервера text.server.refreshing = Обновление сервера
text.hosts.none = [lightgray]Локальных игр не обнаружено! text.hosts.none = [lightgray]Локальных игр не обнаружено!
text.host.invalid = [scarlet] Не удается подключиться к хосту. text.host.invalid = [scarlet] Не удается подключиться к хосту.
text.server.friendlyfire = Дружественный огонь
text.trace = Слежка за игроком
text.trace.playername = Имя игрока: [accent]{0}
text.trace.ip = IP: [accent]{0}
text.trace.id = Уникальный идентификатор: [accent]{0}
text.trace.android = Клиент Android: [accent]{0}
text.trace.modclient = Пользовательский клиент: [accent]{0}
text.trace.totalblocksbroken = Всего разбитых блоков: [accent]{0}
e.structureblocksbroken = Структурных блоков сломанных: [accent]{0}
text.trace.lastblockbroken = Последний сломанный блок:[accent]{0}
text.trace.totalblocksplaced = Всего размещено блоков: [accent]{0}
text.trace.lastblockplaced = Последний размещенный блок: [accent]{0}
text.invalidid = Недопустимый идентификатор клиента! Отправьте отчет об ошибке.
text.server.bans = Блокировки
text.server.bans.none = Никаких заблокированных игроков не найдено!
text.server.admins = Администраторы
text.server.admins.none = Администраторов не найдено!
text.server.add = Добавить сервер text.server.add = Добавить сервер
text.server.delete = Вы действительно хотите удалить этот сервер? text.server.delete = Вы действительно хотите удалить этот сервер?
text.server.hostname = Хост: {0} text.server.hostname = Хост: {0}
text.server.edit = Редактировать сервер text.server.edit = Редактировать сервер
text.server.outdated = [crimson]Устаревший сервер![]
text.server.outdated.client = [crimson]Устаревший клиент![]
text.server.version = [lightgray]Версия: {0}
text.server.custombuild=[yellow]Пользовательская сборка
text.confirmban = Вы действительно хотите заблокировать этого игрока?
text.confirmunban = Вы действительно хотите разблокировать этого игрока?
text.confirmadmin = Вы уверены, что хотите сделать этого игрока администратором?
text.confirmunadmin = Вы действительно хотите удалить статус администратора с этого игрока?
text.joingame.byip = Присоединиться по IP ... text.joingame.byip = Присоединиться по IP ...
text.joingame.title = Присоединиться к игре text.joingame.title = Присоединиться к игре
text.joingame.ip = IP: text.joingame.ip = IP:
text.disconnect = Отключен\n text.disconnect = Отключён\n
text.disconnect.data = Не удалось загрузить данные мира!
text.connecting = [accent]Подключение... text.connecting = [accent]Подключение...
text.connecting.data = [accent]Загрузка данных мира... text.connecting.data = [accent]Загрузка данных мира...
text.connectfail = [crimson]Не удалось подключиться к серверу: [orange] {0} text.connectfail = [crimson]Не удалось подключиться к серверу: [orange] {0}
text.server.port = Порт: text.server.port = Порт:
text.server.addressinuse=Адрес уже используется!
text.server.invalidport = Неверный номер порта! text.server.invalidport = Неверный номер порта!
text.server.error = [crimson]Ошибка создания сервера: [orange] {0} text.server.error = [crimson]Ошибка создания сервера: [orange] {0}
text.tutorial.back = <назад text.tutorial.back = <назад
@@ -73,6 +122,7 @@ text.off = Выкл
text.save.autosave = Автосохранение: {0} text.save.autosave = Автосохранение: {0}
text.save.map = Карта: {0} text.save.map = Карта: {0}
text.save.wave = Волна: {0} text.save.wave = Волна: {0}
text.save.difficulty = Сложность: {0}
text.save.date = Последнее сохранение: {0} text.save.date = Последнее сохранение: {0}
text.confirm = Подтвердить text.confirm = Подтвердить
text.delete = Удалить text.delete = Удалить
@@ -80,8 +130,16 @@ text.ok = ОК
text.open = Открыть text.open = Открыть
text.cancel = Отмена text.cancel = Отмена
text.openlink = Открыть ссылку text.openlink = Открыть ссылку
text.copylink = Скопировать ссылку
text.back = Назад text.back = Назад
text.quit.confirm = Вы уверены, что хотите выйти? text.quit.confirm = Вы уверены, что хотите выйти?
text.changelog.title = Список изменений
text.changelog.loading = Получение изменений ...
text.changelog.error.android = [orange]Обратите внимание, что журнал изменений иногда не работает на Android 4.4 и ниже!\nЭто связано с внутренней ошибкой Android.
text.changelog.error.ios = [orange]В настоящее время журнал изменений не поддерживается iOS.
text.changelog.error = [scarlet]Ошибка при получении изменений!\nПроверьте подключение к Интернету.
text.changelog.current=[yellow][[Текущая версия]
text.changelog.latest=[orange][[Последняя версия]
text.loading = [accent] Загрузка... text.loading = [accent] Загрузка...
text.wave = [orange]Волна {0} text.wave = [orange]Волна {0}
text.wave.waiting = Волна через {0} text.wave.waiting = Волна через {0}
@@ -90,6 +148,7 @@ text.enemies = {0} Противников
text.enemies.single = {0} Противник text.enemies.single = {0} Противник
text.loadimage = Загрузить изображение text.loadimage = Загрузить изображение
text.saveimage = Сохранить изображение text.saveimage = Сохранить изображение
text.oregen = Генерация руд
text.editor.badsize = [orange]Недопустимый формат изображения! [] \nДопустимый формат карты: {0} text.editor.badsize = [orange]Недопустимый формат изображения! [] \nДопустимый формат карты: {0}
text.editor.errorimageload = Ошибка загрузки изображения: [orange] {0} text.editor.errorimageload = Ошибка загрузки изображения: [orange] {0}
text.editor.errorimagesave = Ошибка сохранения изображения: [orange] {0} text.editor.errorimagesave = Ошибка сохранения изображения: [orange] {0}
@@ -119,6 +178,8 @@ text.menu = Меню
text.play = Играть text.play = Играть
text.load = Загрузить text.load = Загрузить
text.save = Сохранить text.save = Сохранить
text.language.restart = Перезагрузите игру, чтобы настройки языка вступили в силу.
text.settings.language = Язык
text.settings = Настройки text.settings = Настройки
text.tutorial = Обучение text.tutorial = Обучение
text.editor = Редактор text.editor = Редактор
@@ -134,6 +195,7 @@ text.purchased = [LIME]Создан!
text.weapons = Оружие text.weapons = Оружие
text.paused = Пауза text.paused = Пауза
text.respawn = Возрождение через text.respawn = Возрождение через
text.info.title = [accent]Информация
text.error.title = [crimson]Произошла ошибка text.error.title = [crimson]Произошла ошибка
text.error.crashmessage = [SCARLET]Произошла непредвиденная ошибка,которая могла вызвать сбой.[]Пожалуйста, сообщите точные обстоятельства разработчику,при которых эта ошибка возникла : [ORANGE]anukendev@gmail.com[] text.error.crashmessage = [SCARLET]Произошла непредвиденная ошибка,которая могла вызвать сбой.[]Пожалуйста, сообщите точные обстоятельства разработчику,при которых эта ошибка возникла : [ORANGE]anukendev@gmail.com[]
text.error.crashtitle = Произошла ошибка text.error.crashtitle = Произошла ошибка
@@ -206,9 +268,12 @@ setting.effects.name = Эффекты на экране
setting.sensitivity.name = Чувствительность контроллера setting.sensitivity.name = Чувствительность контроллера
setting.saveinterval.name = Интервал автосохранения setting.saveinterval.name = Интервал автосохранения
setting.seconds = {0} Секунд setting.seconds = {0} Секунд
setting.fullscreen.name = Полноэкранный
setting.multithread.name = Многопоточность
setting.fps.name = Показать FPS setting.fps.name = Показать FPS
setting.vsync.name = Верт. синхронизация setting.vsync.name = Верт. синхронизация
setting.lasers.name = Показывать энергетические лазеры setting.lasers.name = Показывать энергетические лазеры
setting.previewopacity.name = Прозрачность объкта при предв. просм.
setting.healthbars.name = Показать полоски здоровья объекта setting.healthbars.name = Показать полоски здоровья объекта
setting.pixelate.name = Пикселизация экрана setting.pixelate.name = Пикселизация экрана
setting.musicvol.name = Громкость музыки setting.musicvol.name = Громкость музыки
@@ -270,17 +335,23 @@ tutorial.healingTurret.text = Этот лазер можно использов
tutorial.healingTurretExplain.text = Пока она имеет заряд, эта турель будет [lime]ремонтировать соседние блоки.[] Когда вы играете, убедитесь, что вы имеете такую на своей базе как можно быстрее. tutorial.healingTurretExplain.text = Пока она имеет заряд, эта турель будет [lime]ремонтировать соседние блоки.[] Когда вы играете, убедитесь, что вы имеете такую на своей базе как можно быстрее.
tutorial.smeltery.text = Для многих блоков требуется [orange]сталь[], для этого требуется [orange]плавильный завод[]. Поместите его сюда. tutorial.smeltery.text = Для многих блоков требуется [orange]сталь[], для этого требуется [orange]плавильный завод[]. Поместите его сюда.
tutorial.smelterySetup.text = Этот завод теперь производит [orange]сталь[] из поступающего железа, используя уголь в качестве топлива. tutorial.smelterySetup.text = Этот завод теперь производит [orange]сталь[] из поступающего железа, используя уголь в качестве топлива.
tutorial.tunnelExplain.text = Также обратите внимание, что предметы проходят через [orange] туннельный блок [] и появляются на другой стороне, проходя через каменный блок. Имейте в виду, что туннели могут проходить только до двух блоков.
tutorial.end.text = На этом обучение закончено! Удачи! tutorial.end.text = На этом обучение закончено! Удачи!
text.keybind.title = Переназначить клавиши
keybind.move_x.name = движение_x keybind.move_x.name = движение_x
keybind.move_y.name = движение_y keybind.move_y.name = движение_y
keybind.select.name = выбрать keybind.select.name = выбрать
keybind.break.name = Разрушить keybind.break.name = Разрушить
keybind.shootInternal.name = Выстрелить keybind.shoot.name = стрельба
keybind.zoom_hold.name = масштаб_удерживать keybind.zoom_hold.name = удержаниеума
keybind.zoom.name = масштаб keybind.zoom.name = Приблизить
keybind.block_info.name = инфо_о_блоке
keybind.menu.name = Меню keybind.menu.name = Меню
keybind.pause.name = Пауза keybind.pause.name = Пауза
keybind.dash.name = Рывок keybind.dash.name = Рывок
keybind.chat.name = Чат
keybind.player_list.name = список_игроков
keybind.console.name = консоль
keybind.rotate_alt.name = вращать_alt keybind.rotate_alt.name = вращать_alt
keybind.rotate.name = вращать keybind.rotate.name = вращать
keybind.weapon_1.name = Оружие_1 keybind.weapon_1.name = Оружие_1
@@ -289,9 +360,13 @@ keybind.weapon_3.name = Оружие_3
keybind.weapon_4.name = Оружие_4 keybind.weapon_4.name = Оружие_4
keybind.weapon_5.name = Оружие_5 keybind.weapon_5.name = Оружие_5
keybind.weapon_6.name = Оружие_6 keybind.weapon_6.name = Оружие_6
mode.text.help.title=Описание режимов
mode.waves.name = волны mode.waves.name = волны
mode.waves.description = в нормальном режиме. ограниченные ресурсы и автоматические наступающие волны.
mode.sandbox.name = песочница mode.sandbox.name = песочница
mode.sandbox.description = бесконечные ресурсы и нет таймера для волн.
mode.freebuild.name = свободная\nстройка mode.freebuild.name = свободная\nстройка
mode.freebuild.description=ограниченные ресурсы и нет таймера для волн.
upgrade.standard.name = стандарт upgrade.standard.name = стандарт
upgrade.standard.description = Стандартный мех. upgrade.standard.description = Стандартный мех.
upgrade.blaster.name = Бластер upgrade.blaster.name = Бластер
@@ -319,6 +394,7 @@ liquid.plasma.name = Плазма
liquid.lava.name = лава liquid.lava.name = лава
liquid.oil.name = Нефть liquid.oil.name = Нефть
block.weaponfactory.name = оружейный завод block.weaponfactory.name = оружейный завод
block.weaponfactory.fulldescription=Используется для создания оружия для игрока. Нажмите для использования. Автоматически извлекает ресурсы из ядра.
block.air.name = воздух block.air.name = воздух
block.blockpart.name = часть блока block.blockpart.name = часть блока
block.deepwater.name = глубоководье block.deepwater.name = глубоководье

BIN
core/assets/music/1.mp3 Normal file → Executable file

Binary file not shown.

BIN
core/assets/music/2.mp3 Normal file → Executable file

Binary file not shown.

BIN
core/assets/music/3.mp3 Normal file → Executable file

Binary file not shown.

BIN
core/assets/music/4.mp3 Normal file → Executable file

Binary file not shown.

BIN
core/assets/music/5.mp3 Executable file

Binary file not shown.

BIN
core/assets/music/6.mp3 Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,9 +1,13 @@
package io.anuke.mindustry; package io.anuke.mindustry;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Filter;
import com.badlogic.gdx.graphics.PixmapIO;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.io.BlockLoader; import io.anuke.mindustry.io.BlockLoader;
import io.anuke.mindustry.io.BundleLoader; import io.anuke.mindustry.io.BundleLoader;
import io.anuke.mindustry.io.Platform;
import io.anuke.ucore.modules.ModuleCore; import io.anuke.ucore.modules.ModuleCore;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;

View File

@@ -4,13 +4,16 @@ import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.Bullet;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.effect.Shield; import io.anuke.mindustry.entities.effect.Shield;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.EditLog;
import io.anuke.mindustry.net.ClientDebug; import io.anuke.mindustry.net.ClientDebug;
import io.anuke.mindustry.net.ServerDebug; import io.anuke.mindustry.net.ServerDebug;
import io.anuke.ucore.UCore; import io.anuke.ucore.UCore;
@@ -18,7 +21,7 @@ import io.anuke.ucore.entities.EffectEntity;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.OS;
import java.util.Locale; import java.util.Locale;
public class Vars{ public class Vars{
@@ -49,12 +52,17 @@ public class Vars{
public static final String discordURL = "https://discord.gg/BKADYds"; public static final String discordURL = "https://discord.gg/BKADYds";
public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases"; public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases";
public static final String macAppDir = UCore.getProperty("user.home") + "/Library/Application Support/";
//directory for user-created map data //directory for user-created map data
public static final FileHandle customMapDirectory = gwt ? null : UCore.isAssets() ? public static final FileHandle customMapDirectory = gwt ? null : UCore.isAssets() ?
Gdx.files.local("../../desktop/mindustry-maps") : Gdx.files.local("mindustry-maps/"); Gdx.files.local("../../desktop/mindustry-maps") :
OS.isMac ? (Gdx.files.absolute(macAppDir).child("maps/")) :
Gdx.files.local("mindustry-maps/");
//save file directory //save file directory
public static final FileHandle saveDirectory = gwt ? null : UCore.isAssets() ? public static final FileHandle saveDirectory = gwt ? null : UCore.isAssets() ?
Gdx.files.local("../../desktop/mindustry-saves") : Gdx.files.local("mindustry-saves/"); Gdx.files.local("../../desktop/mindustry-saves") :
OS.isMac ? (Gdx.files.absolute(macAppDir).child("saves/")) :
Gdx.files.local("mindustry-saves/");
//scale of the font //scale of the font
public static float fontscale = Math.max(Unit.dp.scl(1f)/2f, 0.5f); public static float fontscale = Math.max(Unit.dp.scl(1f)/2f, 0.5f);
//camera zoom displayed on startup //camera zoom displayed on startup
@@ -90,12 +98,14 @@ public class Vars{
//amount of drops that are left when breaking a block //amount of drops that are left when breaking a block
public static final float breakDropAmount = 0.5f; public static final float breakDropAmount = 0.5f;
public static Array<EditLog> currentEditLogs = new Array<>();
//only if smoothCamera //only if smoothCamera
public static boolean snapCamera = true; public static boolean snapCamera = true;
public static final int tilesize = 8; public static final int tilesize = 8;
public static final Locale[] locales = {new Locale("en"), new Locale("fr"), new Locale("ru"), new Locale("uk", "UA"), new Locale("pl", "PL"), public static final Locale[] locales = {new Locale("en"), new Locale("fr"), new Locale("ru"), new Locale("uk", "UA"), new Locale("pl"),
new Locale("de"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID"), new Locale("ita"), new Locale("es")}; new Locale("de"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID"), new Locale("ita"), new Locale("es")};
public static final Color[] playerColors = { public static final Color[] playerColors = {

View File

@@ -13,7 +13,6 @@ import io.anuke.mindustry.input.AndroidInput;
import io.anuke.mindustry.input.DefaultKeybinds; import io.anuke.mindustry.input.DefaultKeybinds;
import io.anuke.mindustry.input.DesktopInput; import io.anuke.mindustry.input.DesktopInput;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.io.Saves; import io.anuke.mindustry.io.Saves;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.Item;
@@ -25,7 +24,9 @@ import io.anuke.ucore.core.Inputs.DeviceType;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.modules.Module; import io.anuke.ucore.modules.Module;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.*; import io.anuke.ucore.util.Atlas;
import io.anuke.ucore.util.InputProxy;
import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -102,7 +103,7 @@ public class Control extends Module{
Sounds.setFalloff(9000f); Sounds.setFalloff(9000f);
Musics.load("1.mp3", "2.mp3", "3.mp3", "4.mp3"); Musics.load("1.mp3", "2.mp3", "3.mp3", "4.mp3", "5.mp3", "6.mp3");
DefaultKeybinds.load(); DefaultKeybinds.load();
@@ -385,7 +386,7 @@ public class Control extends Module{
if(respawntime > 0){ if(respawntime > 0){
respawntime -= delta(); respawntime -= Timers.delta();
if(respawntime <= 0){ if(respawntime <= 0){
player.set(world.getSpawnX(), world.getSpawnY()); player.set(world.getSpawnX(), world.getSpawnY());

View File

@@ -131,14 +131,14 @@ public class Logic extends Module {
if(!state.mode.disableWaveTimer){ if(!state.mode.disableWaveTimer){
if(state.enemies <= 0){ if(state.enemies <= 0){
if(!world.getMap().name.equals("tutorial")) state.wavetime -= delta(); if(!world.getMap().name.equals("tutorial")) state.wavetime -= Timers.delta();
if(state.lastUpdated < state.wave + 1 && state.wavetime < aheadPathfinding){ //start updating beforehand if(state.lastUpdated < state.wave + 1 && state.wavetime < aheadPathfinding){ //start updating beforehand
world.pathfinder().resetPaths(); world.pathfinder().resetPaths();
state.lastUpdated = state.wave + 1; state.lastUpdated = state.wave + 1;
} }
}else if(!world.getMap().name.equals("tutorial")){ }else if(!world.getMap().name.equals("tutorial")){
state.extrawavetime -= delta(); state.extrawavetime -= Timers.delta();
} }
} }

View File

@@ -4,13 +4,13 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.IntSet; import com.badlogic.gdx.utils.IntSet;
import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.Bullet;
import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.net.Net; 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;
@@ -168,6 +168,10 @@ public class NetClient extends Module {
ui.hudfrag.updateItems(); ui.hudfrag.updateItems();
}); });
Net.handleClient(BlockLogRequestPacket.class, packet -> {
currentEditLogs = packet.editlogs;
});
Net.handleClient(PlacePacket.class, (packet) -> { Net.handleClient(PlacePacket.class, (packet) -> {
Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, Timers.get("placeblocksound", 10)); Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, Timers.get("placeblocksound", 10));

View File

@@ -5,7 +5,6 @@ import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.game.EventType.GameOverEvent; import io.anuke.mindustry.game.EventType.GameOverEvent;
import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Administration.PlayerInfo; import io.anuke.mindustry.net.Administration.PlayerInfo;
@@ -14,6 +13,7 @@ import io.anuke.mindustry.net.Packets.*;
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;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Events; import io.anuke.ucore.core.Events;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
@@ -25,7 +25,6 @@ import io.anuke.ucore.util.Timer;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class NetServer extends Module{ public class NetServer extends Module{
@@ -44,7 +43,10 @@ public class NetServer extends Module{
public NetServer(){ public NetServer(){
Events.on(GameOverEvent.class, () -> weapons.clear()); Events.on(GameOverEvent.class, () -> {
weapons.clear();
admins.getEditLogs().clear();
});
Net.handleServer(Connect.class, (id, connect) -> { Net.handleServer(Connect.class, (id, connect) -> {
if(admins.isIPBanned(connect.addressTCP)){ if(admins.isIPBanned(connect.addressTCP)){
@@ -199,10 +201,19 @@ public class NetServer extends Module{
if(recipe == null) return; if(recipe == null) return;
Tile tile = world.tile(packet.x, packet.y);
if(tile.synthetic() && admins.isValidateReplace() && !admins.validateBreak(admins.getTrace(Net.getConnection(id).address).uuid, Net.getConnection(id).address)){
if(Timers.get("break-message-" + id, 120)){
sendMessageTo(id, "[scarlet]Anti-grief: you are replacing blocks too quickly. wait until replacing again.");
}
return;
}
state.inventory.removeItems(recipe.requirements); state.inventory.removeItems(recipe.requirements);
Placement.placeBlock(packet.x, packet.y, block, packet.rotation, true, false); Placement.placeBlock(packet.x, packet.y, block, packet.rotation, true, false);
admins.logEdit(packet.x, packet.y, connections.get(id), block, packet.rotation, EditLog.EditAction.PLACE);
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 ++; admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlockPlaced ++;
@@ -215,9 +226,19 @@ public class NetServer extends Module{
if(!Placement.validBreak(packet.x, packet.y)) return; if(!Placement.validBreak(packet.x, packet.y)) return;
Tile tile = world.tile(packet.x, packet.y);
if(tile.synthetic() && !admins.validateBreak(admins.getTrace(Net.getConnection(id).address).uuid, Net.getConnection(id).address)){
if(Timers.get("break-message-" + id, 120)){
sendMessageTo(id, "[scarlet]Anti-grief: you are breaking blocks too quickly. wait until breaking again.");
}
return;
}
Block block = Placement.breakBlock(packet.x, packet.y, true, false); Block block = Placement.breakBlock(packet.x, packet.y, true, false);
if(block != null) { if(block != null) {
admins.logEdit(packet.x, packet.y, connections.get(id), block, tile.getRotation(), EditLog.EditAction.BREAK);
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 ++; admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlocksBroken ++;
@@ -328,6 +349,24 @@ public class NetServer extends Module{
Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name); Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name);
} }
}); });
Net.handleServer(BlockLogRequestPacket.class, (id, packet) -> {
packet.editlogs = admins.getEditLogs().get(packet.x + packet.y * world.width(), new Array<>());
Net.sendTo(id, packet, SendMode.udp);
});
Net.handleServer(RollbackRequestPacket.class, (id, packet) -> {
Player player = connections.get(id);
if(!player.isAdmin){
Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform a rollback without proper security access.",
player.name, Net.getConnection(player.clientid).address);
return;
}
admins.rollbackWorld(packet.rollbackTimes);
Log.info("&lc{0} has rolled back the world {1} times.", player.name, packet.rollbackTimes);
});
} }
public void update(){ public void update(){
@@ -376,6 +415,12 @@ public class NetServer extends Module{
admins.save(); admins.save();
} }
void sendMessageTo(int id, String message){
ChatPacket packet = new ChatPacket();
packet.text = message;
Net.sendTo(id, packet, SendMode.tcp);
}
void sync(){ void sync(){
if(timer.get(timerEntitySync, serverSyncTime)){ if(timer.get(timerEntitySync, serverSyncTime)){

View File

@@ -1,10 +1,12 @@
package io.anuke.mindustry.io; package io.anuke.mindustry.core;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Base64Coder; import com.badlogic.gdx.utils.Base64Coder;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.entities.Entity; import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.TextField;
import java.util.Date; import java.util.Date;
@@ -12,26 +14,40 @@ import java.util.Locale;
import java.util.Random; import java.util.Random;
public abstract class Platform { public abstract class Platform {
/**Each separate game platform should set this instance to their own implementation.*/
public static Platform instance = new Platform() {}; public static Platform instance = new Platform() {};
/**Format the date using the default date formatter.*/
public String format(Date date){return "invalid";} public String format(Date date){return "invalid";}
/**Format a number by adding in commas or periods where needed.*/
public String format(int number){return "invalid";} public String format(int number){return "invalid";}
/**Show a native error dialog.*/
public void showError(String text){} public void showError(String text){}
/**Add a text input dialog that should show up after the field is tapped.*/
public void addDialog(TextField field){ public void addDialog(TextField field){
addDialog(field, 16); addDialog(field, 16);
} }
/**See addDialog().*/
public void addDialog(TextField field, int maxLength){} public void addDialog(TextField field, int maxLength){}
/**Update discord RPC.*/
public void updateRPC(){} public void updateRPC(){}
/**Called when the game is exited.*/
public void onGameExit(){} public void onGameExit(){}
/**Open donation dialog. Currently android only.*/
public void openDonations(){} public void openDonations(){}
/**Whether discord RPC is supported.*/
public boolean hasDiscord(){return true;} public boolean hasDiscord(){return true;}
/**Request Android permissions for writing files.*/
public void requestWritePerms(){} public void requestWritePerms(){}
/**Return the localized name for the locale. This is basically a workaround for GWT not supporting getName().*/
public String getLocaleName(Locale locale){ public String getLocaleName(Locale locale){
return locale.toString(); return locale.toString();
} }
/**Whether joining games is supported.*/
public boolean canJoinGame(){ public boolean canJoinGame(){
return true; return true;
} }
/**Whether debug mode is enabled.*/
public boolean isDebug(){return false;} public boolean isDebug(){return false;}
/**Must be 8 bytes in length.*/ /**Must be 8 bytes in length.*/
public byte[] getUUID(){ public byte[] getUUID(){
@@ -46,6 +62,19 @@ public abstract class Platform {
} }
return Base64Coder.decode(uuid); return Base64Coder.decode(uuid);
} }
/**Only used for iOS or android: open the share menu for a map or save.*/
public void shareFile(FileHandle file){}
/**Show a file chooser. Desktop only.
*
* @param text File chooser title text
* @param content Type of files to be loaded
* @param cons Selection listener
* @param open Whether to open or save files.
* @param filetype File extensions to filter.
*/
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filetype){}
/**Use the default thread provider from the kryonet module for this.*/
public ThreadProvider getThreadProvider(){ public ThreadProvider getThreadProvider(){
return new ThreadProvider() { return new ThreadProvider() {
@Override public boolean isOnThread() {return true;} @Override public boolean isOnThread() {return true;}

View File

@@ -490,6 +490,12 @@ public class Renderer extends RendererModule{
Draw.color(); Draw.color();
} }
if(Inputs.keyDown("block_logs")){
Draw.color(Colors.get("accent"));
Lines.crect(target.drawx(), target.drawy(), target.block().width * tilesize, target.block().height * tilesize);
Draw.color();
}
if(target.entity != null) { if(target.entity != null) {
int bot = 0, top = 0; int bot = 0, top = 0;
for (BlockBar bar : target.block().bars) { for (BlockBar bar : target.block().bars) {

View File

@@ -7,7 +7,6 @@ import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Align;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.mapeditor.MapEditorDialog; import io.anuke.mindustry.mapeditor.MapEditorDialog;
import io.anuke.mindustry.ui.dialogs.*; import io.anuke.mindustry.ui.dialogs.*;
import io.anuke.mindustry.ui.fragments.*; import io.anuke.mindustry.ui.fragments.*;
@@ -49,6 +48,7 @@ public class UI extends SceneModule{
public BansDialog bans; public BansDialog bans;
public AdminsDialog admins; public AdminsDialog admins;
public TraceDialog traces; public TraceDialog traces;
public RollbackDialog rollback;
public ChangelogDialog changelog; public ChangelogDialog changelog;
public final MenuFragment menufrag = new MenuFragment(); public final MenuFragment menufrag = new MenuFragment();
@@ -160,6 +160,7 @@ public class UI extends SceneModule{
bans = new BansDialog(); bans = new BansDialog();
admins = new AdminsDialog(); admins = new AdminsDialog();
traces = new TraceDialog(); traces = new TraceDialog();
rollback = new RollbackDialog();
build.begin(scene); build.begin(scene);

View File

@@ -25,6 +25,7 @@ public class DefaultKeybinds {
"rotate", new Axis(Input.SCROLL), "rotate", new Axis(Input.SCROLL),
"toggle_menus", Input.C, "toggle_menus", Input.C,
"block_info", Input.CONTROL_LEFT, "block_info", Input.CONTROL_LEFT,
"block_logs", Input.I,
"player_list", Input.TAB, "player_list", Input.TAB,
"chat", Input.ENTER, "chat", Input.ENTER,
"chat_history_prev", Input.UP, "chat_history_prev", Input.UP,

View File

@@ -108,6 +108,17 @@ public class DesktopInput extends InputHandler{
} }
} }
if(recipe == null && !ui.hasMouse() && Inputs.keyDown("block_logs")) {
showCursor = true;
if(Inputs.keyTap("select")){
NetEvents.handleBlockLogRequest(getBlockX(), getBlockY());
Timers.runTask(20f, () -> {
ui.hudfrag.blockfrag.showBlockLogs(getBlockX(), getBlockY());
Cursors.restoreCursor();
});
}
}
if(target != null && target.block().isConfigurable(target)){ if(target != null && target.block().isConfigurable(target)){
showCursor = true; showCursor = true;
} }

View File

@@ -46,9 +46,9 @@ public class GestureHandler extends GestureAdapter{
public boolean pan(float x, float y, float deltaX, float deltaY){ public boolean pan(float x, float y, float deltaX, float deltaY){
if(control.showCursor() && !Inputs.keyDown("select")) return false; if(control.showCursor() && !Inputs.keyDown("select")) return false;
if(!control.showCursor() && !(control.input().recipe != null if((!control.showCursor() && !(control.input().recipe != null
&& control.input().placeMode.lockCamera && state.inventory.hasItems(control.input().recipe.requirements)) && && control.input().placeMode.lockCamera && state.inventory.hasItems(control.input().recipe.requirements)) &&
!(control.input().recipe == null && control.input().breakMode.lockCamera)){ !(control.input().recipe == null && control.input().breakMode.lockCamera)) && !ui.hasMouse(x, y)){
float dx = deltaX*Core.camera.zoom/Core.cameraScale, dy = deltaY*Core.camera.zoom/Core.cameraScale; float dx = deltaX*Core.camera.zoom/Core.cameraScale, dy = deltaY*Core.camera.zoom/Core.cameraScale;
player.x -= dx; player.x -= dx;
player.y += dy; player.y += dy;

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.io;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.I18NBundle; import com.badlogic.gdx.utils.I18NBundle;
import io.anuke.mindustry.core.Platform;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.io; package io.anuke.mindustry.io;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.world.Map; import io.anuke.mindustry.world.Map;

View File

@@ -86,7 +86,7 @@ public class Saves {
current = slot; current = slot;
} }
public void importSave(FileHandle file) throws IOException{ public SaveSlot importSave(FileHandle file) throws IOException{
SaveSlot slot = new SaveSlot(nextSlot); SaveSlot slot = new SaveSlot(nextSlot);
slot.importFile(file); slot.importFile(file);
nextSlot ++; nextSlot ++;
@@ -94,6 +94,7 @@ public class Saves {
saves.add(slot); saves.add(slot);
slot.meta = SaveIO.getData(slot.index); slot.meta = SaveIO.getData(slot.index);
current = slot; current = slot;
return slot;
} }
public Array<SaveSlot> getSaveSlots(){ public Array<SaveSlot> getSaveSlots(){

View File

@@ -1,11 +1,11 @@
package io.anuke.mindustry.mapeditor; package io.anuke.mindustry.mapeditor;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.ui.dialogs.FileChooser;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.ColorMapper; import io.anuke.mindustry.world.ColorMapper;
import io.anuke.mindustry.world.ColorMapper.BlockPair; import io.anuke.mindustry.world.ColorMapper.BlockPair;
@@ -42,7 +42,6 @@ public class MapEditorDialog extends Dialog{
private MapSaveDialog saveDialog; private MapSaveDialog saveDialog;
private MapResizeDialog resizeDialog; private MapResizeDialog resizeDialog;
private ScrollPane pane; private ScrollPane pane;
private FileChooser openFile, saveFile;
private boolean saved = false; private boolean saved = false;
private ButtonGroup<ImageButton> blockgroup; private ButtonGroup<ImageButton> blockgroup;
@@ -55,42 +54,6 @@ public class MapEditorDialog extends Dialog{
dialog = new MapGenerateDialog(editor); dialog = new MapGenerateDialog(editor);
view = new MapView(editor); view = new MapView(editor);
openFile = new FileChooser("$text.loadimage", FileChooser.pngFilter, true, file -> {
ui.loadfrag.show();
Timers.run(3f, () -> {
try{
Pixmap pixmap = new Pixmap(file);
if(verifySize(pixmap)){
editor.setPixmap(pixmap);
view.clearStack();
}else{
ui.showError(Bundles.format("text.editor.badsize", Arrays.toString(MapEditor.validMapSizes)));
}
}catch (Exception e){
ui.showError(Bundles.format("text.editor.errorimageload", Strings.parseException(e, false)));
Log.err(e);
}
ui.loadfrag.hide();
});
});
saveFile = new FileChooser("$saveimage", false, file -> {
if(!file.extension().toLowerCase().equals(".png")){
file = file.parent().child(file.nameWithoutExtension() + ".png");
}
FileHandle result = file;
ui.loadfrag.show();
Timers.run(3f, () -> {
try{
Pixmaps.write(editor.pixmap(), result);
}catch (Exception e){
ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false)));
if(!mobile) Log.err(e);
}
ui.loadfrag.hide();
});
});
loadDialog = new MapLoadDialog(map -> { loadDialog = new MapLoadDialog(map -> {
saveDialog.setFieldText(map.name); saveDialog.setFieldText(map.name);
ui.loadfrag.show(); ui.loadfrag.show();
@@ -238,15 +201,45 @@ public class MapEditorDialog extends Dialog{
row(); row();
new imagebutton("icon-load-image", isize, () -> //iOS does not support loading raw files.
openFile.show() if(!ios) {
).text("$text.editor.loadimage");
row(); new imagebutton("icon-load-image", isize, () -> {
Platform.instance.showFileChooser(Bundles.get("text.loadimage"), "Image Files", MapEditorDialog.this::tryLoadMap, true, "png");
}).text("$text.editor.loadimage");
new imagebutton("icon-save-image", isize, () -> row();
saveFile.show() }
).text("$text.editor.saveimage");
new imagebutton("icon-save-image", isize, () -> {
//iOS doesn't really support saving raw files. Sharing is used instead.
if(!ios){
Platform.instance.showFileChooser(Bundles.get("text.saveimage"), "Image Files", file -> {
if(!file.extension().toLowerCase().equals(".png")){
file = file.parent().child(file.nameWithoutExtension() + ".png");
}
FileHandle result = file;
ui.loadfrag.show();
Timers.run(3f, () -> {
try{
Pixmaps.write(editor.pixmap(), result);
}catch (Exception e){
ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false)));
if(!mobile) Log.err(e);
}
ui.loadfrag.hide();
});
}, false, "png");
}else{
try{
FileHandle file = Gdx.files.local(("map-" + ((editor.getMap().name == null) ? "unknown" : editor.getMap().name) + ".png"));
Pixmaps.write(editor.pixmap(), file);
Platform.instance.shareFile(file);
}catch (Exception e){
ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false)));
}
}
}).text("$text.editor.saveimage");
row(); row();
@@ -329,6 +322,25 @@ public class MapEditorDialog extends Dialog{
}}.grow().end(); }}.grow().end();
} }
public void tryLoadMap(FileHandle file){
ui.loadfrag.show();
Timers.runTask(3f, () -> {
try{
Pixmap pixmap = new Pixmap(file);
if(verifySize(pixmap)){
editor.setPixmap(pixmap);
view.clearStack();
}else{
ui.showError(Bundles.format("text.editor.badsize", Arrays.toString(MapEditor.validMapSizes)));
}
}catch (Exception e){
ui.showError(Bundles.format("text.editor.errorimageload", Strings.parseException(e, false)));
Log.err(e);
}
ui.loadfrag.hide();
});
}
private void doInput(){ private void doInput(){
//tool select //tool select
for(int i = 0; i < EditorTool.values().length; i ++){ for(int i = 0; i < EditorTool.values().length; i ++){

View File

@@ -1,6 +1,6 @@
package io.anuke.mindustry.mapeditor; package io.anuke.mindustry.mapeditor;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Map; import io.anuke.mindustry.world.Map;
import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Consumer;

View File

@@ -1,25 +1,163 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.Json; import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Placement;
import io.anuke.mindustry.world.blocks.types.BlockPart;
import io.anuke.mindustry.world.blocks.types.Floor;
import io.anuke.mindustry.world.blocks.types.Rock;
import io.anuke.mindustry.world.blocks.types.StaticBlock;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import static io.anuke.mindustry.Vars.world;
public class Administration { public class Administration {
public static final int defaultMaxBrokenBlocks = 15;
public static final int defaultBreakCooldown = 1000*15;
private Json json = new Json(); private Json json = new Json();
/**All player info. Maps UUIDs to info. This persists throughout restarts.*/ /**All player info. Maps UUIDs to info. This persists throughout restarts.*/
private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>(); private ObjectMap<String, PlayerInfo> playerInfo = new ObjectMap<>();
/**Maps UUIDs to trace infos. This is wiped when a player logs off.*/ /**Maps UUIDs to trace infos. This is wiped when a player logs off.*/
private ObjectMap<String, TraceInfo> traceInfo = new ObjectMap<>(); private ObjectMap<String, TraceInfo> traceInfo = new ObjectMap<>();
/**Maps packed coordinates to logs for that coordinate */
private IntMap<Array<EditLog>> editLogs = new IntMap<>();
private Array<String> bannedIPs = new Array<>(); private Array<String> bannedIPs = new Array<>();
public Administration(){ public Administration(){
Settings.defaults("playerInfo", "{}"); Settings.defaultList(
Settings.defaults("bannedIPs", "{}"); "playerInfo", "{}",
"bannedIPs", "{}",
"antigrief", false,
"antigrief-max", defaultMaxBrokenBlocks,
"antigrief-cooldown", defaultBreakCooldown
);
load(); load();
} }
public boolean isAntiGrief(){
return Settings.getBool("antigrief");
}
public boolean isValidateReplace(){
return false;
}
public void setAntiGrief(boolean antiGrief){
Settings.putBool("antigrief", antiGrief);
Settings.save();
}
public void setAntiGriefParams(int maxBreak, int cooldown){
Settings.putInt("antigrief-max", maxBreak);
Settings.putInt("antigrief-cooldown", cooldown);
Settings.save();
}
public IntMap<Array<EditLog>> getEditLogs() {
return editLogs;
}
public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action) {
if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock) return;
if(editLogs.containsKey(x + y * world.width())) {
editLogs.get(x + y * world.width()).add(new EditLog(player.name, block, rotation, action));
}
else {
Array<EditLog> logs = new Array<>();
logs.add(new EditLog(player.name, block, rotation, action));
editLogs.put(x + y * world.width(), logs);
}
}
public void rollbackWorld(int rollbackTimes) {
for(IntMap.Entry<Array<EditLog>> editLog : editLogs.entries()) {
int coords = editLog.key;
Array<EditLog> logs = editLog.value;
for(int i = 0; i < rollbackTimes; i++) {
EditLog log = logs.get(logs.size - 1);
int x = coords % world.width();
int y = coords / world.width();
Block result = log.block;
int rotation = log.rotation;
if(log.action == EditLog.EditAction.PLACE) {
Placement.breakBlock(x, y, false, false);
Packets.BreakPacket packet = new Packets.BreakPacket();
packet.x = (short) x;
packet.y = (short) y;
packet.playerid = 0;
Net.send(packet, Net.SendMode.tcp);
}
else if(log.action == EditLog.EditAction.BREAK) {
Placement.placeBlock(x, y, result, rotation, false, false);
Packets.PlacePacket packet = new Packets.PlacePacket();
packet.x = (short) x;
packet.y = (short) y;
packet.rotation = (byte) rotation;
packet.playerid = 0;
packet.block = result.id;
Net.send(packet, Net.SendMode.tcp);
}
logs.removeIndex(logs.size - 1);
if(logs.size == 0) {
editLogs.remove(coords);
break;
}
}
}
}
public boolean validateBreak(String id, String ip){
if(!isAntiGrief() || isAdmin(id, ip)) return true;
PlayerInfo info = getCreateInfo(id);
if(info.lastBroken == null || info.lastBroken.length != Settings.getInt("antigrief-max")){
info.lastBroken = new long[Settings.getInt("antigrief-max")];
}
long[] breaks = info.lastBroken;
int shiftBy = 0;
for(int i = 0; i < breaks.length && breaks[i] != 0; i ++){
if(TimeUtils.timeSinceMillis(breaks[i]) >= Settings.getInt("antigrief-cooldown")){
shiftBy = i;
}
}
for (int i = 0; i < breaks.length; i++) {
breaks[i] = (i + shiftBy >= breaks.length) ? 0 : breaks[i + shiftBy];
}
int remaining = 0;
for(int i = 0; i < breaks.length; i ++){
if(breaks[i] == 0){
remaining = breaks.length - i;
break;
}
}
if(remaining == 0) return false;
breaks[breaks.length - remaining] = TimeUtils.millis();
return true;
}
/**Call when a player joins to update their information here.*/ /**Call when a player joins to update their information here.*/
public void updatePlayerJoined(String id, String ip, String name){ public void updatePlayerJoined(String id, String ip, String name){
PlayerInfo info = getCreateInfo(id); PlayerInfo info = getCreateInfo(id);
@@ -248,6 +386,8 @@ public class Administration {
public boolean banned, admin; public boolean banned, admin;
public long lastKicked; //last kicked timestamp public long lastKicked; //last kicked timestamp
public long[] lastBroken;
PlayerInfo(String id){ PlayerInfo(String id){
this.id = id; this.id = id;
} }

View File

@@ -0,0 +1,26 @@
package io.anuke.mindustry.net;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.world.Block;
public class EditLog {
public String playername;
public Block block;
public int rotation;
public EditAction action;
EditLog(String playername, Block block, int rotation, EditAction action) {
this.playername = playername;
this.block = block;
this.rotation = rotation;
this.action = action;
}
public String info() {
return String.format("Player: %s, Block: %s, Rotation: %s, Edit Action: %s", playername, block.name(), rotation, action.toString());
}
public enum EditAction {
PLACE, BREAK;
}
}

View File

@@ -9,7 +9,7 @@ import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.Packet.ImportantPacket; import io.anuke.mindustry.net.Packet.ImportantPacket;
import io.anuke.mindustry.net.Packet.UnimportantPacket; import io.anuke.mindustry.net.Packet.UnimportantPacket;
import io.anuke.mindustry.net.Streamable.StreamBegin; import io.anuke.mindustry.net.Streamable.StreamBegin;

View File

@@ -177,4 +177,20 @@ public class NetEvents {
ui.traces.show(target, netServer.admins.getTrace(Net.getConnection(target.clientid).address)); ui.traces.show(target, netServer.admins.getTrace(Net.getConnection(target.clientid).address));
} }
} }
public static void handleBlockLogRequest(int x, int y) {
BlockLogRequestPacket packet = new BlockLogRequestPacket();
packet.x = x;
packet.y = y;
packet.editlogs = Vars.currentEditLogs;
Net.send(packet, SendMode.udp);
}
public static void handleRollbackRequest(int rollbackTimes) {
RollbackRequestPacket packet = new RollbackRequestPacket();
packet.rollbackTimes = rollbackTimes;
Net.send(packet, SendMode.udp);
}
} }

View File

@@ -1,8 +1,11 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Base64Coder; import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.ReflectionException; import com.badlogic.gdx.utils.reflect.ReflectionException;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
@@ -12,7 +15,6 @@ import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/**Class for storing all packets.*/ /**Class for storing all packets.*/
@@ -140,6 +142,60 @@ public class Packets {
} }
} }
public static class BlockLogRequestPacket implements Packet {
public int x;
public int y;
public Array<EditLog> editlogs;
@Override
public void write(ByteBuffer buffer) {
buffer.putShort((short)x);
buffer.putShort((short)y);
buffer.putInt(editlogs.size);
for(EditLog value : editlogs) {
buffer.put((byte)value.playername.getBytes().length);
buffer.put(value.playername.getBytes());
buffer.putInt(value.block.id);
buffer.put((byte) value.rotation);
buffer.put((byte) value.action.ordinal());
}
}
@Override
public void read(ByteBuffer buffer) {
x = buffer.getShort();
y = buffer.getShort();
editlogs = new Array<>();
int arraySize = buffer.getInt();
for(int a = 0; a < arraySize; a ++) {
byte length = buffer.get();
byte[] bytes = new byte[length];
buffer.get(bytes);
String name = new String(bytes);
int blockid = buffer.getInt();
int rotation = buffer.get();
int ordinal = buffer.get();
editlogs.add(new EditLog(name, Block.getByID(blockid), rotation, EditLog.EditAction.values()[ordinal]));
}
}
}
public static class RollbackRequestPacket implements Packet {
public int rollbackTimes;
@Override
public void write(ByteBuffer buffer) {
buffer.putInt(rollbackTimes);
}
@Override
public void read(ByteBuffer buffer) {
rollbackTimes = buffer.getInt();
}
}
public static class PositionPacket implements Packet{ public static class PositionPacket implements Packet{
public byte[] data; public byte[] data;

View File

@@ -17,6 +17,8 @@ public class Registrator {
PlacePacket.class, PlacePacket.class,
BreakPacket.class, BreakPacket.class,
StateSyncPacket.class, StateSyncPacket.class,
BlockLogRequestPacket.class,
RollbackRequestPacket.class,
BlockSyncPacket.class, BlockSyncPacket.class,
BulletPacket.class, BulletPacket.class,
EnemyDeathPacket.class, EnemyDeathPacket.class,
@@ -44,7 +46,7 @@ public class Registrator {
NetErrorPacket.class, NetErrorPacket.class,
PlayerAdminPacket.class, PlayerAdminPacket.class,
AdministerRequestPacket.class, AdministerRequestPacket.class,
TracePacket.class, TracePacket.class
}; };
private static ObjectIntMap<Class<?>> ids = new ObjectIntMap<>(); private static ObjectIntMap<Class<?>> ids = new ObjectIntMap<>();

View File

@@ -2,16 +2,20 @@ package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.ObjectSet;
import io.anuke.mindustry.ui.Links; import io.anuke.mindustry.ui.Links;
import io.anuke.mindustry.ui.Links.LinkEntry; import io.anuke.mindustry.ui.Links.LinkEntry;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
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;
import io.anuke.ucore.util.OS;
import static io.anuke.mindustry.Vars.ios;
import static io.anuke.mindustry.Vars.ui; import static io.anuke.mindustry.Vars.ui;
public class AboutDialog extends FloatingDialog { public class AboutDialog extends FloatingDialog {
private static ObjectSet<String> bannedItems = ObjectSet.with("google-play", "itch.io", "dev-builds", "trello");
public AboutDialog(){ public AboutDialog(){
super("$text.about.button"); super("$text.about.button");
@@ -25,6 +29,10 @@ public class AboutDialog extends FloatingDialog {
ScrollPane pane = new ScrollPane(in, "clear"); ScrollPane pane = new ScrollPane(in, "clear");
for(LinkEntry link : Links.getLinks()){ for(LinkEntry link : Links.getLinks()){
if((ios || OS.isMac) && bannedItems.contains(link.name)){ //because Apple doesn't like me mentioning things
continue;
}
Table table = new Table("button"); Table table = new Table("button");
table.margin(0); table.margin(0);
table.table(img -> { table.table(img -> {
@@ -59,10 +67,14 @@ public class AboutDialog extends FloatingDialog {
content().add(pane).growX(); content().add(pane).growX();
buttons().addButton("$text.credits", this::showCredits).size(200f, 64f); buttons().addButton("$text.credits", this::showCredits).size(200f, 64f);
buttons().addButton("$text.changelog.title", ui.changelog::show).size(200f, 64f);
if(!ios && !OS.isMac){
buttons().addButton("$text.changelog.title", ui.changelog::show).size(200f, 64f);
}
} }
private void showCredits(){ public void showCredits(){
FloatingDialog dialog = new FloatingDialog("$text.credits"); FloatingDialog dialog = new FloatingDialog("$text.credits");
dialog.addCloseButton(); dialog.addCloseButton();
dialog.content().add("$text.about"); dialog.content().add("$text.about");

View File

@@ -10,6 +10,9 @@ import io.anuke.ucore.core.Settings;
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;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.OS;
import static io.anuke.mindustry.Vars.ios;
public class ChangelogDialog extends FloatingDialog{ public class ChangelogDialog extends FloatingDialog{
private final float vw = 600; private final float vw = 600;
@@ -22,13 +25,15 @@ public class ChangelogDialog extends FloatingDialog{
content().add("$text.changelog.loading"); content().add("$text.changelog.loading");
Changelogs.getChangelog(result -> { if(!ios && !OS.isMac) {
versions = result; Changelogs.getChangelog(result -> {
Gdx.app.postRunnable(this::setup); versions = result;
}, t -> { Gdx.app.postRunnable(this::setup);
Log.err(t); }, t -> {
Gdx.app.postRunnable(this::setup); Log.err(t);
}); Gdx.app.postRunnable(this::setup);
});
}
} }
void setup(){ void setup(){
@@ -45,12 +50,16 @@ public class ChangelogDialog extends FloatingDialog{
table.add("$text.changelog.error.android").padTop(8); table.add("$text.changelog.error.android").padTop(8);
} }
if(Vars.ios){ if(ios){
table.row(); table.row();
table.add("$text.changelog.error.ios").padTop(8); table.add("$text.changelog.error.ios").padTop(8);
} }
}else{ }else{
for(VersionInfo info : versions){ for(VersionInfo info : versions){
String desc = info.description;
desc = desc.replace("Android", "Mobile");
Table in = new Table("clear"); Table in = new Table("clear");
in.top().left().margin(10); in.top().left().margin(10);
@@ -63,7 +72,7 @@ public class ChangelogDialog extends FloatingDialog{
in.add("$text.changelog.latest"); in.add("$text.changelog.latest");
} }
in.row(); in.row();
in.labelWrap("[lightgray]" + info.description).width(vw - 20).padTop(12); in.labelWrap("[lightgray]" + desc).width(vw - 20).padTop(12);
table.add(in).width(vw).pad(8).row(); table.add(in).width(vw).pad(8).row();
} }

View File

@@ -6,7 +6,8 @@ import com.badlogic.gdx.graphics.g2d.GlyphLayout;
import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pools; import com.badlogic.gdx.utils.Pools;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Consumer;
@@ -15,12 +16,14 @@ import io.anuke.ucore.scene.event.Touchable;
import io.anuke.ucore.scene.ui.*; import io.anuke.ucore.scene.ui.*;
import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.OS;
import java.util.Arrays; import java.util.Arrays;
public class FileChooser extends FloatingDialog { public class FileChooser extends FloatingDialog {
private Table files; private Table files;
private FileHandle homeDirectory = Gdx.files.absolute(Gdx.files.getExternalStoragePath()); private FileHandle homeDirectory = Gdx.files.absolute(OS.isMac ? UCore.getProperty("user.home") + "/Downloads/" :
Gdx.files.getExternalStoragePath());
private FileHandle directory = homeDirectory; private FileHandle directory = homeDirectory;
private ScrollPane pane; private ScrollPane pane;
private TextField navigation, filefield; private TextField navigation, filefield;
@@ -96,6 +99,11 @@ public class FileChooser extends FloatingDialog {
updateFiles(true); updateFiles(true);
}); });
//Macs are confined to the Downloads/ directory
if(OS.isMac){
up.setDisabled(true);
}
ImageButton back = new ImageButton("icon-arrow-left"); ImageButton back = new ImageButton("icon-arrow-left");
back.resizeImage(isize); back.resizeImage(isize);
@@ -166,7 +174,8 @@ public class FileChooser extends FloatingDialog {
private void updateFiles(boolean push){ private void updateFiles(boolean push){
if(push) stack.push(directory); if(push) stack.push(directory);
navigation.setText(directory.toString()); //if is mac, don't display extra info since you can only ever go to downloads
navigation.setText(OS.isMac ? directory.name() : directory.toString());
GlyphLayout layout = Pools.obtain(GlyphLayout.class); GlyphLayout layout = Pools.obtain(GlyphLayout.class);
@@ -181,23 +190,25 @@ public class FileChooser extends FloatingDialog {
Pools.free(layout); Pools.free(layout);
files.clearChildren(); files.clearChildren();
files.top().left();
FileHandle[] names = getFileNames(); FileHandle[] names = getFileNames();
Image upimage = new Image("icon-folder-parent"); //macs are confined to the Downloads/ directory
if(!OS.isMac) {
Image upimage = new Image("icon-folder-parent");
TextButton upbutton = new TextButton(".." + directory.toString());
upbutton.clicked(() -> {
directory = directory.parent();
updateFiles(true);
});
TextButton upbutton = new TextButton(".." + directory.toString()); upbutton.left().add(upimage).padRight(4f).size(14 * 2);
upbutton.clicked(()->{ upbutton.getLabel().setAlignment(Align.left);
directory = directory.parent(); upbutton.getCells().reverse();
updateFiles(true);
});
upbutton.left().add(upimage).padRight(4f).size(14*2); files.add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2);
upbutton.getCells().reverse(); files.row();
}
files.top().left().add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2);
upbutton.getLabel().setAlignment(Align.left);
files.row();
ButtonGroup<TextButton> group = new ButtonGroup<TextButton>(); ButtonGroup<TextButton> group = new ButtonGroup<TextButton>();
group.setMinCheckCount(0); group.setMinCheckCount(0);

View File

@@ -4,7 +4,7 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.net.Host; import io.anuke.mindustry.net.Host;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;

View File

@@ -1,6 +1,6 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.scene.ui.ButtonGroup; import io.anuke.ucore.scene.ui.ButtonGroup;
import io.anuke.ucore.scene.ui.ScrollPane; import io.anuke.ucore.scene.ui.ScrollPane;

View File

@@ -1,7 +1,10 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.SaveIO; import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.io.Saves.SaveSlot; import io.anuke.mindustry.io.Saves.SaveSlot;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
@@ -83,14 +86,24 @@ public class LoadDialog extends FloatingDialog{
if(!gwt) { if(!gwt) {
t.addImageButton("icon-save", "empty", 14 * 3, () -> { t.addImageButton("icon-save", "empty", 14 * 3, () -> {
new FileChooser("$text.save.export", false, file -> { if(!ios) {
try { Platform.instance.showFileChooser(Bundles.get("text.save.export"), "Mindustry Save", file -> {
slot.exportFile(file); try {
setup(); slot.exportFile(file);
} catch (IOException e) { setup();
ui.showError(Bundles.format("text.save.export.fail", Strings.parseException(e, false))); } catch (IOException e) {
} ui.showError(Bundles.format("text.save.export.fail", Strings.parseException(e, false)));
}).show(); }
}, false, "mins");
}else{
try {
FileHandle file = Gdx.files.local("save-" + slot.getName() + ".mins");
slot.exportFile(file);
Platform.instance.shareFile(file);
}catch (Exception e){
ui.showError(Bundles.format("text.save.export.fail", Strings.parseException(e, false)));
}
}
}).size(14 * 3).right(); }).size(14 * 3).right();
} }
@@ -134,10 +147,10 @@ public class LoadDialog extends FloatingDialog{
slots.row(); slots.row();
if(gwt) return; if(gwt || ios) return;
slots.addImageTextButton("$text.save.import", "icon-add", "clear", 14*3, () -> { slots.addImageTextButton("$text.save.import", "icon-add", "clear", 14*3, () -> {
new FileChooser("$text.save.import", f -> f.extension().equals("mins"), true, file -> { Platform.instance.showFileChooser(Bundles.get("text.save.import"), "Mindustry Save", file -> {
if(SaveIO.isSaveValid(file)){ if(SaveIO.isSaveValid(file)){
try{ try{
control.getSaves().importSave(file); control.getSaves().importSave(file);
@@ -148,30 +161,34 @@ public class LoadDialog extends FloatingDialog{
}else{ }else{
ui.showError("$text.save.import.invalid"); ui.showError("$text.save.import.invalid");
} }
}).show(); }, true, "mins");
}).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4); }).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4);
} }
public void runLoadSave(SaveSlot slot){
ui.loadfrag.show();
Timers.runTask(3f, () -> {
ui.loadfrag.hide();
hide();
try{
slot.load();
state.set(State.playing);
ui.paused.hide();
}catch(Exception e){
Log.err(e);
ui.paused.hide();
state.set(State.menu);
logic.reset();
ui.showError("$text.save.corrupted");
}
});
}
public void modifyButton(TextButton button, SaveSlot slot){ public void modifyButton(TextButton button, SaveSlot slot){
button.clicked(() -> { button.clicked(() -> {
if(!button.childrenPressed()){ if(!button.childrenPressed()){
ui.loadfrag.show(); runLoadSave(slot);
Timers.runTask(3f, () -> {
ui.loadfrag.hide();
hide();
try{
slot.load();
state.set(State.playing);
ui.paused.hide();
}catch(Exception e){
Log.err(e);
ui.paused.hide();
state.set(State.menu);
logic.reset();
ui.showError("$text.save.corrupted");
}
});
} }
}); });
} }

View File

@@ -0,0 +1,40 @@
package io.anuke.mindustry.ui.dialogs;
import io.anuke.mindustry.net.NetEvents;
import io.anuke.ucore.scene.ui.Label;
import io.anuke.ucore.scene.ui.TextField;
import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Strings;
import static io.anuke.mindustry.Vars.*;
public class RollbackDialog extends FloatingDialog {
public RollbackDialog(){
super("$text.server.rollback");
setup();
shown(this::setup);
}
private void setup(){
content().clear();
buttons().clear();
if(gwt) return;
content().row();
content().add("$text.server.rollback.numberfield");
TextField field = content().addField("", t->{}).size(200f, 48f).get();
field.setTextFieldFilter((f, c) -> field.getText().length() < 4);
content().row();
buttons().defaults().size(200f, 50f).left().pad(2f);
buttons().addButton("$text.cancel", this::hide);
buttons().addButton("$text.ok", () -> {
NetEvents.handleRollbackRequest(Integer.valueOf(field.getText()));
hide();
}).disabled(b -> field.getText().isEmpty() || !Strings.canParsePostiveInt(field.getText()));
}
}

View File

@@ -7,6 +7,7 @@ import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.input.InputHandler; import io.anuke.mindustry.input.InputHandler;
import io.anuke.mindustry.net.EditLog;
import io.anuke.mindustry.resource.*; import io.anuke.mindustry.resource.*;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
@@ -24,7 +25,6 @@ import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class BlocksFragment implements Fragment{ public class BlocksFragment implements Fragment{
@@ -345,6 +345,44 @@ public class BlocksFragment implements Fragment{
d.show(); d.show();
} }
public void showBlockLogs(int x, int y){
boolean wasPaused = state.is(State.paused);
state.set(State.paused);
FloatingDialog d = new FloatingDialog("$text.blocks.editlogs");
Table table = new Table();
table.defaults().pad(1f);
ScrollPane pane = new ScrollPane(table, "clear");
pane.setFadeScrollBars(false);
Table top = new Table();
top.left();
top.add("[accent]Edit logs for: "+ x + ", " + y);
table.add(top).fill().left();
table.row();
d.content().add(pane).grow();
if(currentEditLogs == null || currentEditLogs.size == 0) {
table.add("$text.block.editlogsnotfound").left();
table.row();
}
else {
for(int i = 0; i < currentEditLogs.size; i++) {
EditLog log = currentEditLogs.get(i);
table.add("[gold]" + (i + 1) + ". [white]" + log.info()).left();
table.row();
}
}
d.buttons().addButton("$text.ok", () -> {
if(!wasPaused)
state.set(State.playing);
d.hide();
}).size(110, 50).pad(10f);
d.show();
}
public void updateItems(){ public void updateItems(){
itemtable.clear(); itemtable.clear();

View File

@@ -8,7 +8,7 @@ import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.net.NetEvents;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;

View File

@@ -48,7 +48,7 @@ public class HudFragment implements Fragment{
flip = new imagebutton("icon-arrow-up", isize, () -> toggleMenus()).get(); flip = new imagebutton("icon-arrow-up", isize, () -> toggleMenus()).get();
update(t -> { update(t -> {
if(Inputs.keyTap("toggle_menus")){ if(Inputs.keyTap("toggle_menus") && !ui.chatfrag.chatOpen()){
toggleMenus(); toggleMenus();
} }
}); });

View File

@@ -2,13 +2,14 @@ package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.Version; import io.anuke.mindustry.io.Version;
import io.anuke.mindustry.ui.MenuButton; import io.anuke.mindustry.ui.MenuButton;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table; import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.util.OS;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -44,7 +45,13 @@ public class MenuFragment implements Fragment{
add(new MenuButton("icon-info", "$text.about.button", ui.about::show)); add(new MenuButton("icon-info", "$text.about.button", ui.about::show));
add(new MenuButton("icon-menu", "$text.changelog.title", ui.changelog::show)); add(new MenuButton("icon-menu", OS.isMac ? "$text.credits" : "$text.changelog.title", () -> {
if(OS.isMac){
ui.about.showCredits();
}else {
ui.changelog.show();
}
}));
row(); row();
@@ -57,7 +64,8 @@ public class MenuFragment implements Fragment{
}else { }else {
new table() {{ new table() {{
defaults().size(120f).pad(5); float size = 120f;
defaults().size(size).pad(5);
float isize = 14f * 4; float isize = 14f * 4;
new imagebutton("icon-play-2", isize, ui.levels::show).text("$text.play").padTop(4f); new imagebutton("icon-play-2", isize, ui.levels::show).text("$text.play").padTop(4f);
@@ -70,13 +78,21 @@ public class MenuFragment implements Fragment{
row(); row();
new imagebutton("icon-editor", isize, ui.editor::show).text("$text.editor").padTop(4f); new table(){{
new imagebutton("icon-tools", isize, ui.settings::show).text("$text.settings").padTop(4f); defaults().size(size).pad(5);
new imagebutton("icon-info", isize, ui.about::show).text("$text.about.button").padTop(4f); new imagebutton("icon-editor", isize, ui.editor::show).text("$text.editor").padTop(4f);
new imagebutton("icon-donate", isize, Platform.instance::openDonations).text("$text.donate").padTop(4f); new imagebutton("icon-tools", isize, ui.settings::show).text("$text.settings").padTop(4f);
new imagebutton("icon-info", isize, ui.about::show).text("$text.about.button").padTop(4f);
if (!ios) {
new imagebutton("icon-donate", isize, Platform.instance::openDonations).text("$text.donate").padTop(4f);
}
}}.colspan(4).end();
}}.end(); }}.end();
} }
}}.end(); }}.end();

View File

@@ -33,6 +33,7 @@ public class PlayerListFragment implements Fragment{
public void build(){ public void build(){
new table(){{ new table(){{
new table("pane"){{ new table("pane"){{
touchable(Touchable.enabled);
margin(14f); margin(14f);
new label(() -> Bundles.format(playerGroup.size() == 1 ? "text.players.single" : new label(() -> Bundles.format(playerGroup.size() == 1 ? "text.players.single" :
"text.players", playerGroup.size())); "text.players", playerGroup.size()));
@@ -59,6 +60,10 @@ public class PlayerListFragment implements Fragment{
ui.admins.show(); ui.admins.show();
}).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> Net.client()); }).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> Net.client());
new button("$text.server.rollback", () -> {
ui.rollback.show();
}).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> !player.isAdmin);
}}.pad(10f).growX().end(); }}.pad(10f).growX().end();
}}.end(); }}.end();

View File

@@ -181,6 +181,11 @@ public class Tile{
return isLinked() || !((floor.solid && (block == Blocks.air || block.solidifes)) || (block.solid && (!block.destructible && !block.update))); return isLinked() || !((floor.solid && (block == Blocks.air || block.solidifes)) || (block.solid && (!block.destructible && !block.update)));
} }
public boolean synthetic(){
Block block = block();
return block.update || block.destructible;
}
public boolean solid(){ public boolean solid(){
Block block = block(); Block block = block();
Block floor = floor(); Block floor = floor();

View File

@@ -84,16 +84,17 @@ task packrCmd() {
exec { exec {
commandLine "java", "-jar", PACKR_DIR + "packr.jar", commandLine("java", "-jar", PACKR_DIR + "packr.jar",
"--verbose", "--verbose",
"--bundle", getPackage(), "--bundle", getPackage() + ".mac",
"--platform", getPlatform(), "--platform", getPlatform(),
"--executable", appName, "--executable", appName,
"--output", "packr-out/", "--output", "packr-out/",
"--mainclass", project.ext.mainClassName, "--mainclass", project.ext.mainClassName,
"--jdk", PACKR_DIR + "jdk-" + getPlatform() + ".zip", "--jdk", PACKR_DIR + "jdk-" + getPlatform() + ".zip",
"--icon", ICON_DIR.getAbsolutePath(), "--icon", ICON_DIR.getAbsolutePath(),
"--classpath", "--", PACKR_DIR + "config.json" "--vmargs", (getPlatform().equals("mac") ? "XstartOnFirstThread" : "Xms256m"),
"--classpath", "--", PACKR_DIR + "config.json")
} }
} }
} }

View File

@@ -1,12 +1,15 @@
package io.anuke.mindustry.desktop; package io.anuke.mindustry.desktop;
import com.badlogic.gdx.Files.FileType;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import io.anuke.kryonet.KryoClient; import io.anuke.kryonet.KryoClient;
import io.anuke.kryonet.KryoServer; import io.anuke.kryonet.KryoServer;
import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.UCore;
import io.anuke.ucore.util.OS;
public class DesktopLauncher { public class DesktopLauncher {
@@ -18,6 +21,10 @@ public class DesktopLauncher {
config.setWindowedMode(960, 540); config.setWindowedMode(960, 540);
config.setWindowIcon("sprites/icon.png"); config.setWindowIcon("sprites/icon.png");
if(OS.isMac) {
config.setPreferencesConfig(UCore.getProperty("user.home") + "/Library/Application Support/Mindustry", FileType.Absolute);
}
Platform.instance = new DesktopPlatform(arg); Platform.instance = new DesktopPlatform(arg);
Net.setClientProvider(new KryoClient()); Net.setClientProvider(new KryoClient());

View File

@@ -3,14 +3,17 @@ package io.anuke.mindustry.desktop;
import club.minnced.discord.rpc.DiscordEventHandlers; import club.minnced.discord.rpc.DiscordEventHandlers;
import club.minnced.discord.rpc.DiscordRPC; import club.minnced.discord.rpc.DiscordRPC;
import club.minnced.discord.rpc.DiscordRichPresence; import club.minnced.discord.rpc.DiscordRichPresence;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Base64Coder; import com.badlogic.gdx.utils.Base64Coder;
import io.anuke.kryonet.DefaultThreadImpl; import io.anuke.kryonet.DefaultThreadImpl;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.ui.dialogs.FileChooser;
import io.anuke.ucore.UCore; import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import javax.swing.*; import javax.swing.*;
@@ -40,6 +43,11 @@ public class DesktopPlatform extends Platform {
} }
} }
@Override
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filter) {
new FileChooser(text, file -> file.extension().equalsIgnoreCase(filter), open, cons).show();
}
@Override @Override
public String format(Date date){ public String format(Date date){
return format.format(date); return format.format(date);

View File

@@ -1,6 +1,5 @@
#Thu May 03 13:02:25 EDT 2018
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.5-bin.zip

View File

@@ -16,7 +16,7 @@ import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.i18n.shared.DateTimeFormat; import com.google.gwt.i18n.shared.DateTimeFormat;
import com.google.gwt.user.client.ui.*; import com.google.gwt.user.client.ui.*;
import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;

View File

@@ -6,7 +6,7 @@ import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.badlogic.gdx.utils.reflect.ReflectionException; import com.badlogic.gdx.utils.reflect.ReflectionException;
import com.sksamuel.gwt.websockets.Websocket; import com.sksamuel.gwt.websockets.Websocket;
import com.sksamuel.gwt.websockets.WebsocketListener; import com.sksamuel.gwt.websockets.WebsocketListener;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.ClientProvider; import io.anuke.mindustry.net.Net.ClientProvider;
import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Net.SendMode;

View File

@@ -12,12 +12,16 @@
<string>${app.id}</string> <string>${app.id}</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>MinimumOSVersion</key>
<string>9.0.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>${app.name}</string> <string>${app.name}</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>${app.version}</string> <string>${app.version}</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
@@ -28,6 +32,10 @@
<false/> <false/>
<key>UIStatusBarHidden</key> <key>UIStatusBarHidden</key>
<true/> <true/>
<key>UIRequiresFullScreen</key>
<true/>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Mindustry</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>
@@ -43,16 +51,60 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>CFBundleIcons</key>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeIconFiles</key>
<array>
<string>icon-72.png</string>
</array>
<key>CFBundleTypeName</key>
<string>Mindustry Map File</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Owner</string>
<key>LSItemContentTypes</key>
<array>
<string>public.png</string>
<string>io.anuke.mindustry.savefile</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Mindustry Save File</string>
<key>UTTypeIdentifier</key>
<string>io.anuke.mindustry.savefile</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<string>mins</string>
<key>public.mime-type</key>
<string>mindustry/mins</string>
</dict>
</dict>
</array>
<!--<key>CFBundleIcons</key>
<dict> <dict>
<key>CFBundlePrimaryIcon</key> <key>CFBundlePrimaryIcon</key>
<dict> <dict>
<key>CFBundleIconFiles</key> <key>CFBundleIconFiles</key>
<array> <array>
<string>Icon</string> <string>icon</string>
<string>Icon-72</string> <string>icon-72</string>
<string>icon-120</string>
<string>icon-152</string>
</array> </array>
</dict> </dict>
</dict> </dict>-->
</dict> </dict>
</plist> </plist>

View File

@@ -0,0 +1,116 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon-40.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon-60.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "icon-87.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon-80.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "icon-121.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-120.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-180.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon-20.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon-42.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon-29.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon-58.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon-41.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon-81.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-77.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-152.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "icon-167.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "icon-main.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

BIN
ios/data/icon-120.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
ios/data/icon-152.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
ios/data/icon-167.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
ios/data/icon-180.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
ios/data/icon-20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 810 B

BIN
ios/data/icon-29.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
ios/data/icon-40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
ios/data/icon-58.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
ios/data/icon-60.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
ios/data/icon-76.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
ios/data/icon-80.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
ios/data/icon-87.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
ios/data/icon-main.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,6 +1,6 @@
app.version=1.0 app.version=3.5
app.id=io.anuke.mindustry app.id=io.anuke.mindustry
app.mainclass=io.anuke.mindustry.IOSLauncher app.mainclass=io.anuke.mindustry.IOSLauncher
app.executable=IOSLauncher app.executable=IOSLauncher
app.build=1 app.build=11
app.name=Mindustry app.name=Mindustry

View File

@@ -1,24 +1,37 @@
package io.anuke.mindustry; package io.anuke.mindustry;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.iosrobovm.IOSApplication; import com.badlogic.gdx.backends.iosrobovm.IOSApplication;
import com.badlogic.gdx.backends.iosrobovm.IOSApplicationConfiguration; import com.badlogic.gdx.backends.iosrobovm.IOSApplicationConfiguration;
import com.badlogic.gdx.files.FileHandle;
import io.anuke.kryonet.DefaultThreadImpl; import io.anuke.kryonet.DefaultThreadImpl;
import io.anuke.kryonet.KryoClient; import io.anuke.kryonet.KryoClient;
import io.anuke.kryonet.KryoServer; import io.anuke.kryonet.KryoServer;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.core.ThreadHandler; import io.anuke.mindustry.core.ThreadHandler;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.io.Saves.SaveSlot;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.TextField;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Strings;
import org.robovm.apple.foundation.NSAutoreleasePool; import org.robovm.apple.foundation.NSAutoreleasePool;
import org.robovm.apple.uikit.UIApplication; import org.robovm.apple.foundation.NSURL;
import org.robovm.apple.uikit.*;
import java.io.IOException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import static io.anuke.mindustry.Vars.control;
import static io.anuke.mindustry.Vars.ui;
import static org.robovm.apple.foundation.NSPathUtilities.getDocumentsDirectory;
public class IOSLauncher extends IOSApplication.Delegate { public class IOSLauncher extends IOSApplication.Delegate {
@Override @Override
protected IOSApplication createApplication() { protected IOSApplication createApplication() {
@@ -27,6 +40,10 @@ public class IOSLauncher extends IOSApplication.Delegate {
Unit.dp.addition -= 0.2f; Unit.dp.addition -= 0.2f;
if(UIDevice.getCurrentDevice().getUserInterfaceIdiom() == UIUserInterfaceIdiom.Pad){
Unit.dp.addition = 0.5f;
}
Platform.instance = new Platform() { Platform.instance = new Platform() {
DateFormat format = SimpleDateFormat.getDateTimeInstance(); DateFormat format = SimpleDateFormat.getDateTimeInstance();
@@ -59,12 +76,72 @@ public class IOSLauncher extends IOSApplication.Delegate {
public ThreadHandler.ThreadProvider getThreadProvider() { public ThreadHandler.ThreadProvider getThreadProvider() {
return new DefaultThreadImpl(); return new DefaultThreadImpl();
} }
@Override
public void shareFile(FileHandle file){
FileHandle to = Gdx.files.absolute(getDocumentsDirectory()).child(file.name());
file.copyTo(to);
NSURL url = new NSURL(to.file());
UIActivityViewController p = new UIActivityViewController(Collections.singletonList(url), null);
p.getPopoverPresentationController().setSourceView(UIApplication.getSharedApplication().getKeyWindow().getRootViewController().getView());
UIApplication.getSharedApplication().getKeyWindow().getRootViewController()
.presentViewController(p, true, () -> io.anuke.ucore.util.Log.info("Success! Presented {0}", to));
}
}; };
IOSApplicationConfiguration config = new IOSApplicationConfiguration(); IOSApplicationConfiguration config = new IOSApplicationConfiguration();
return new IOSApplication(new Mindustry(), config); return new IOSApplication(new Mindustry(), config);
} }
@Override
public boolean openURL(UIApplication app, NSURL url, UIApplicationOpenURLOptions options) {
System.out.println("Opened URL: " + url.getPath());
openURL(url);
return false;
}
@Override
public boolean didFinishLaunching(UIApplication application, UIApplicationLaunchOptions options) {
boolean b = super.didFinishLaunching(application, options);
if(options != null && options.has(UIApplicationLaunchOptions.Keys.URL())){
System.out.println("Opened URL at launch: " + ((NSURL)options.get(UIApplicationLaunchOptions.Keys.URL())).getPath());
openURL(((NSURL)options.get(UIApplicationLaunchOptions.Keys.URL())));
}
return b;
}
void openURL(NSURL url){
Gdx.app.postRunnable(() -> {
FileHandle file = Gdx.files.absolute(getDocumentsDirectory()).child(url.getLastPathComponent());
Gdx.files.absolute(url.getPath()).copyTo(file);
if(file.extension().equalsIgnoreCase("mins")){ //open save
if(SaveIO.isSaveValid(file)){
try{
SaveSlot slot = control.getSaves().importSave(file);
ui.load.runLoadSave(slot);
}catch (IOException e){
ui.showError(Bundles.format("text.save.import.fail", Strings.parseException(e, false)));
}
}else{
ui.showError("$text.save.import.invalid");
}
}else if(file.extension().equalsIgnoreCase("png")){ //open map
if(!ui.editor.isShown()){
ui.editor.show();
}
ui.editor.tryLoadMap(file);
}
});
}
public static void main(String[] argv) { public static void main(String[] argv) {
NSAutoreleasePool pool = new NSAutoreleasePool(); NSAutoreleasePool pool = new NSAutoreleasePool();
UIApplication.main(argv, null, IOSLauncher.class); UIApplication.main(argv, null, IOSLauncher.class);

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.server;
import com.badlogic.gdx.ApplicationLogger; import com.badlogic.gdx.ApplicationLogger;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.IntMap;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.Difficulty;
@@ -10,15 +11,14 @@ 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.*;
import io.anuke.mindustry.net.Administration.PlayerInfo; 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;
import io.anuke.mindustry.net.Packets.ChatPacket; import io.anuke.mindustry.net.Packets.ChatPacket;
import io.anuke.mindustry.net.Packets.KickReason; import io.anuke.mindustry.net.Packets.KickReason;
import io.anuke.mindustry.net.TraceInfo;
import io.anuke.mindustry.ui.fragments.DebugFragment; import io.anuke.mindustry.ui.fragments.DebugFragment;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Map; import io.anuke.mindustry.world.Map;
import io.anuke.mindustry.world.Placement;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.*; import io.anuke.ucore.core.*;
import io.anuke.ucore.modules.Module; import io.anuke.ucore.modules.Module;
@@ -35,8 +35,6 @@ import java.util.Scanner;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
import static io.anuke.ucore.util.Log.*; import static io.anuke.ucore.util.Log.*;
;
public class ServerControl extends Module { public class ServerControl extends Module {
private final CommandHandler handler = new CommandHandler(""); private final CommandHandler handler = new CommandHandler("");
private ShuffleMode mode; private ShuffleMode mode;
@@ -254,7 +252,7 @@ public class ServerControl extends Module {
} }
}); });
handler.register("friendlyfire", "<on/off>", "Enable or disable friendly fire", arg -> { handler.register("friendlyfire", "<on/off>", "Enable or disable friendly fire.", arg -> {
String s = arg[0]; String s = arg[0];
if(s.equalsIgnoreCase("on")){ if(s.equalsIgnoreCase("on")){
NetEvents.handleFriendlyFireChange(true); NetEvents.handleFriendlyFireChange(true);
@@ -269,6 +267,35 @@ public class ServerControl extends Module {
} }
}); });
handler.register("antigrief", "[on/off] [max-break] [cooldown-in-ms]", "Enable or disable anti-grief.", arg -> {
if(arg.length == 0){
info("Anti-grief is currently &lc{0}.", netServer.admins.isAntiGrief() ? "on" : "off");
return;
}
String s = arg[0];
if(s.equalsIgnoreCase("on")){
netServer.admins.setAntiGrief(true);
info("Anti-grief enabled.");
}else if(s.equalsIgnoreCase("off")){
netServer.admins.setAntiGrief(false);
info("Anti-grief disabled.");
}else{
err("Incorrect command usage.");
}
if(arg.length >= 2) {
try {
int maxbreak = Integer.parseInt(arg[1]);
int cooldown = (arg.length >= 3 ? Integer.parseInt(arg[2]) : Administration.defaultBreakCooldown);
netServer.admins.setAntiGriefParams(maxbreak, cooldown);
info("Anti-grief parameters set.");
} catch (NumberFormatException e) {
err("Invalid number format.");
}
}
});
handler.register("shuffle", "<normal/custom/both/off>", "Set map shuffling.", arg -> { handler.register("shuffle", "<normal/custom/both/off>", "Set map shuffling.", arg -> {
try{ try{
@@ -533,6 +560,10 @@ public class ServerControl extends Module {
boolean found = false; boolean found = false;
for (Player player : playerGroup.all()) { for (Player player : playerGroup.all()) {
if(Net.getConnection(player.clientid) == null){
err("Player \"{0}\" does not have an associated connection!");
continue;
}
TraceInfo info = netServer.admins.getTrace(Net.getConnection(player.clientid).address); TraceInfo info = netServer.admins.getTrace(Net.getConnection(player.clientid).address);
if(info.totalBlocksBroken >= minbreak && info.totalBlocksBroken / Math.max(info.totalBlocksPlaced, 1f) >= ratio){ if(info.totalBlocksBroken >= minbreak && info.totalBlocksBroken / Math.max(info.totalBlocksPlaced, 1f) >= ratio){
info("&ly - Player '{0}' / UUID &lm{1}&ly found: &lc{2}&ly broken and &lc{3}&ly placed.", info("&ly - Player '{0}' / UUID &lm{1}&ly found: &lc{2}&ly broken and &lc{3}&ly placed.",
@@ -697,6 +728,28 @@ public class ServerControl extends Module {
info("Nobody with that name could be found."); info("Nobody with that name could be found.");
} }
}); });
handler.register("rollback", "<amount>", "Rollback the block edits in the world", arg -> {
if(!state.is(State.playing)) {
err("Open the server first.");
return;
}
if(!Strings.canParsePostiveInt(arg[0])) {
err("Please input a valid, positive, number of times to rollback");
return;
}
int rollbackTimes = Integer.valueOf(arg[0]);
IntMap<Array<EditLog>> editLogs = netServer.admins.getEditLogs();
if(editLogs.size == 0){
err("Nothing to rollback!");
return;
}
netServer.admins.rollbackWorld(rollbackTimes);
info("Rollback done!");
});
} }
private void readCommands(){ private void readCommands(){