Merge branch 'master' of https://github.com/Anuken/Mindustry into 4.0
# Conflicts: # core/assets/sprites/sprites.atlas # core/assets/sprites/sprites.png # core/assets/version.properties # core/src/io/anuke/mindustry/ui/fragments/DebugFragment.java
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
android:theme="@style/GdxTheme" >
|
android:theme="@style/GdxTheme" >
|
||||||
<activity
|
<activity
|
||||||
android:name="io.anuke.mindustry.AndroidLauncher"
|
android:name="io.anuke.mindustry.AndroidLauncher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:screenOrientation="sensorLandscape"
|
android:screenOrientation="sensorLandscape"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ allprojects {
|
|||||||
version = 'release'
|
version = 'release'
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
versionNumber = '3.3'
|
versionNumber = '3.4'
|
||||||
versionType = 'release'
|
versionType = 'release'
|
||||||
appName = 'Mindustry'
|
appName = 'Mindustry'
|
||||||
gdxVersion = '1.9.8'
|
gdxVersion = '1.9.8'
|
||||||
aiVersion = '1.8.1'
|
aiVersion = '1.8.1'
|
||||||
uCoreVersion = '23e8c1c'
|
uCoreVersion = '0a15aeb'
|
||||||
|
|
||||||
getVersionString = {
|
getVersionString = {
|
||||||
String buildVersion = getBuildVersion()
|
String buildVersion = getBuildVersion()
|
||||||
|
|||||||
BIN
core/assets-raw/sprites/ui/icons/icon-admin-small.png
Normal file
BIN
core/assets-raw/sprites/ui/icons/icon-admin-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 163 B |
BIN
core/assets-raw/sprites/ui/icons/icon-admin.png
Normal file
BIN
core/assets-raw/sprites/ui/icons/icon-admin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 257 B |
BIN
core/assets-raw/sprites/ui/icons/icon-ban.png
Normal file
BIN
core/assets-raw/sprites/ui/icons/icon-ban.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 276 B |
BIN
core/assets-raw/sprites/ui/icons/icon-zoom-small.png
Normal file
BIN
core/assets-raw/sprites/ui/icons/icon-zoom-small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 228 B |
@@ -25,6 +25,7 @@ text.server.kicked.kick=You have been kicked from the server!
|
|||||||
text.server.kicked.invalidPassword=Invalid password!
|
text.server.kicked.invalidPassword=Invalid password!
|
||||||
text.server.kicked.clientOutdated=Outdated client! Update your game!
|
text.server.kicked.clientOutdated=Outdated client! Update your game!
|
||||||
text.server.kicked.serverOutdated=Outdated server! Ask the host to update!
|
text.server.kicked.serverOutdated=Outdated server! Ask the host to update!
|
||||||
|
text.server.kicked.banned=You are banned on this server.
|
||||||
text.server.connected={0} has joined.
|
text.server.connected={0} has joined.
|
||||||
text.server.disconnected={0} has disconnected.
|
text.server.disconnected={0} has disconnected.
|
||||||
text.nohost=Can't host server on a custom map!
|
text.nohost=Can't host server on a custom map!
|
||||||
@@ -37,10 +38,27 @@ text.server.refreshing=Refreshing server
|
|||||||
text.hosts.none=[lightgray]No LAN games found!
|
text.hosts.none=[lightgray]No LAN games found!
|
||||||
text.host.invalid=[scarlet]Can't connect to host.
|
text.host.invalid=[scarlet]Can't connect to host.
|
||||||
text.server.friendlyfire=Friendly Fire
|
text.server.friendlyfire=Friendly Fire
|
||||||
|
text.trace=Trace Player
|
||||||
|
text.trace.playername=Player name: [accent]{0}
|
||||||
|
text.trace.ip=IP: [accent]{0}
|
||||||
|
text.trace.modclient=Custom Client: [accent]{0}
|
||||||
|
text.trace.totalblocksbroken=Total blocks broken: [accent]{0}
|
||||||
|
text.trace.structureblocksbroken=Structure blocks broken: [accent]{0}
|
||||||
|
text.trace.lastblockbroken=Last block broken: [accent]{0}
|
||||||
|
text.trace.totalblocksplaced=Total blocks placed: [accent]{0}
|
||||||
|
text.trace.lastblockplaced=Last block placed: [accent]{0}
|
||||||
|
text.server.bans=Bans
|
||||||
|
text.server.bans.none=No banned players found!
|
||||||
|
text.server.admins=Admins
|
||||||
|
text.server.admins.none=No admins found!
|
||||||
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}
|
||||||
text.server.edit=Edit Server
|
text.server.edit=Edit Server
|
||||||
|
text.confirmban=Are you sure you want to ban this player?
|
||||||
|
text.confirmunban=Are you sure you want to unban this player?
|
||||||
|
text.confirmadmin=Are you sure you want to make this player an admin?
|
||||||
|
text.confirmunadmin=Are you sure you want to remove admin status from this player?
|
||||||
text.joingame.byip=Join by IP...
|
text.joingame.byip=Join by IP...
|
||||||
text.joingame.title=Join Game
|
text.joingame.title=Join Game
|
||||||
text.joingame.ip=IP:
|
text.joingame.ip=IP:
|
||||||
|
|||||||
479
core/assets/bundles/bundle_de.properties
Normal file
479
core/assets/bundles/bundle_de.properties
Normal file
@@ -0,0 +1,479 @@
|
|||||||
|
text.about = Erstellt von [ROYAL] Anuken. [] \nUrsprünglich ein Eintrag im [orange] GDL [] MM Jam.\n\nCredits: \n- SFX gemacht mit [yellow] bfxr [] - Musik gemacht von [green] RoccoW [] / gefunden auf [lime] FreeMusicArchive.org [] \n\nBesonderer Dank geht an: \n- [coral] MitchellFJN []: Umfangreicher Spieletest und Feedback \n- [sky] Luxray5474 []: Wiki-Arbeit, Code-Beiträge \n- Alle Beta-Tester auf itch.io und Google Play\n
|
||||||
|
text.discord = Trete dem Mindustry Discord bei!
|
||||||
|
text.gameover = Der Kern wurde zerstört.
|
||||||
|
text.highscore = [YELLOW] Neuer Highscore!
|
||||||
|
text.lasted = Du hast bis zur folgenden Welle überlebt
|
||||||
|
text.level.highscore = High Score: [accent] {0}
|
||||||
|
text.level.delete.title = Löschen bestätigen
|
||||||
|
text.level.delete = Bist du sicher, dass du die Karte \"[orange] {0}\" löschen möchtest?
|
||||||
|
text.level.select = Level Auswahl
|
||||||
|
text.level.mode = Spielmodus:
|
||||||
|
text.savegame = Spiel speichern
|
||||||
|
text.loadgame = Spiel laden
|
||||||
|
text.joingame = Spiel beitreten
|
||||||
|
text.quit = Verlassen
|
||||||
|
text.about.button = Info
|
||||||
|
text.name = Name:
|
||||||
|
text.public = Öffentlich
|
||||||
|
text.players = {0} Spieler online
|
||||||
|
text.players.single = {0} Spieler online
|
||||||
|
text.server.mismatch = Paketfehler: Mögliche Client / Server-Version stimmt nicht überein. Stell sicher, dass du und der Host die neueste Version von Mindustry haben!
|
||||||
|
.server.closing = [accent] Server wird geschlossen...
|
||||||
|
text.server.kicked.kick = Du wurdest vom Server gekickt!
|
||||||
|
text.server.kicked.invalidPassword = Falsches Passwort.
|
||||||
|
text.server.connected = {0} ist beigetreten
|
||||||
|
text.server.disconnected = {0} hat die Verbindung getrennt.
|
||||||
|
text.nohost = Server kann nicht auf einer benutzerdefinierten Karte gehostet werden!
|
||||||
|
text.hostserver = Server hosten
|
||||||
|
text.host = Host
|
||||||
|
text.hosting = [accent] Server wird geöffnet...
|
||||||
|
text.hosts.refresh = Aktualisieren
|
||||||
|
text.hosts.discovering = Suche nach LAN-Spielen
|
||||||
|
text.server.refreshing = Server wird aktualisiert
|
||||||
|
text.hosts.none = [lightgray] Keine LAN Spiele gefunden!
|
||||||
|
text.host.invalid = [scarlet] Kann keine Verbindung zum Host herstellen.
|
||||||
|
text.server.add = Server hinzufügen
|
||||||
|
text.server.delete = Bist du dir sicher das du diesen Server löschen möchtest?
|
||||||
|
text.server.hostname = Host: {0}
|
||||||
|
text.server.edit = Server bearbeiten
|
||||||
|
text.joingame.byip = Über IP beitreten ...
|
||||||
|
text.joingame.title = Spiel beitreten
|
||||||
|
text.joingame.ip = IP:
|
||||||
|
text.disconnect = Verbindung unterbrochen.
|
||||||
|
text.connecting = [accent] Verbindet...
|
||||||
|
text.connecting.data = [accent] Weltdaten werden geladen...
|
||||||
|
text.connectfail = [crimson] Verbindung zum Server konnte nicht hergestellt werden: [orange]{0}
|
||||||
|
text.server.port = Port:
|
||||||
|
text.server.invalidport = Falscher Port!
|
||||||
|
text.server.error = [crimson] Fehler beim Hosten des Servers: [orange] {0}
|
||||||
|
text.tutorial.back = < Zurück
|
||||||
|
text.tutorial.next = Weiter >
|
||||||
|
text.save.new = Neuer Spielstand
|
||||||
|
text.save.overwrite = Möchten du diesen Spielstand wirklich überschreiben?
|
||||||
|
text.overwrite = Überschreiben
|
||||||
|
text.save.none = Keine Spielstände gefunden!
|
||||||
|
text.saveload = [accent] Speichern ...
|
||||||
|
text.savefail = Fehler beim Speichern des Spiels!
|
||||||
|
text.save.delete.confirm = Möchtest du diesen Spielstand wirklich löschen?
|
||||||
|
text.save.delete = Löschen
|
||||||
|
text.save.export = Spielstand exportieren
|
||||||
|
text.save.import.invalid = [orange] Dieser Spielstand ist ungültig!
|
||||||
|
text.save.import.fail = [crimson] Spielstand konnte nicht importiert werden: [orange] {0}
|
||||||
|
text.save.export.fail = [crimson] Spielstand konnte nicht exportiert werden: [orange] {0}
|
||||||
|
text.save.import = Spielstand importieren
|
||||||
|
text.save.newslot = Name speichern:
|
||||||
|
text.save.rename = Umbenennen
|
||||||
|
text.save.rename.text = Neuer Name
|
||||||
|
text.selectslot = Wähle einen Spielstand
|
||||||
|
text.slot = [accent] Platz {0}
|
||||||
|
text.save.corrupted = [orange] Datei beschädigt oder ungültig!
|
||||||
|
text.empty = <leer>
|
||||||
|
text.on = An
|
||||||
|
text.off = Aus
|
||||||
|
text.save.autosave = Automatisches Speichern: {0}
|
||||||
|
text.save.map = Karte: {0}
|
||||||
|
text.save.wave = Welle: {0}
|
||||||
|
text.save.date = Zuletzt gespeichert: {0}
|
||||||
|
text.confirm = Bestätigen
|
||||||
|
text.delete = Löschen
|
||||||
|
text.ok = OK
|
||||||
|
text.open = Öffnen
|
||||||
|
text.cancel = Abbruch
|
||||||
|
text.openlink = Link öffnen
|
||||||
|
text.back = Zurück
|
||||||
|
text.quit.confirm = Willst du wirklich aufhören?
|
||||||
|
text.loading = [accent] Wird geladen ...
|
||||||
|
text.wave = [orange] Welle {0}
|
||||||
|
text.wave.waiting = Welle in {0}
|
||||||
|
text.waiting = Warten...
|
||||||
|
text.enemies = {0} Feinde
|
||||||
|
text.enemies.single = {0} Feind
|
||||||
|
text.loadimage = Bild laden
|
||||||
|
text.saveimage = Bild speichern
|
||||||
|
text.editor.badsize = [orange]Ungültige Bildabmessungen! [] Gültige Kartenabmessungen: {0}
|
||||||
|
text.editor.errorimageload = Fehler beim Laden des Bildes: [orange] {0}
|
||||||
|
text.editor.errorimagesave = Fehler beim Speichern des Bildes: [orange] {0}
|
||||||
|
text.editor.generate = Generieren
|
||||||
|
text.editor.resize = Grösse\nanpassen
|
||||||
|
text.editor.loadmap = Karte\nladen
|
||||||
|
text.editor.savemap = Karte\nspeichern
|
||||||
|
text.editor.loadimage = Bild\nladen
|
||||||
|
text.editor.saveimage = Bild\nspeichern
|
||||||
|
text.editor.unsaved = [crimson] Du hast Änderungen nicht gespeichert [] Möchtest du wirklich aufhören?
|
||||||
|
text.editor.brushsize = Pinselgrösse: {0}
|
||||||
|
text.editor.noplayerspawn = Diese Karte hat keinen Spielerspawnpunkt!
|
||||||
|
text.editor.manyplayerspawns = Maps können nicht mehr als einen Spawnpunkt des Spielers haben!
|
||||||
|
text.editor.manyenemyspawns = Kann nicht mehr als {0} feindliche Spawnpunkte haben!
|
||||||
|
text.editor.resizemap = Grösse der Karte ändern
|
||||||
|
text.editor.resizebig = [crimson] Warnung! [] Karten, die grösser als 256 Einheiten sind, können ruckeln und instabil sein.
|
||||||
|
text.editor.mapname = Map Name
|
||||||
|
text.editor.overwrite = [accent] Warnung! Dies überschreibt eine vorhandene Map.
|
||||||
|
text.editor.failoverwrite = [crimson] Die Standardkarte kann nicht überschrieben werden!
|
||||||
|
text.editor.selectmap = Wähle eine Map zum Laden:
|
||||||
|
text.width = Breite:
|
||||||
|
text.height = Höhe:
|
||||||
|
text.randomize = Zufällig
|
||||||
|
text.apply = Anwenden
|
||||||
|
text.update = Aktualisieren
|
||||||
|
text.menu = Menü
|
||||||
|
text.play = Spielen
|
||||||
|
text.load = Laden
|
||||||
|
text.save = Speichern
|
||||||
|
text.settings = Einstellungen
|
||||||
|
text.tutorial = Tutorial
|
||||||
|
text.editor = Bearbeiter
|
||||||
|
text.mapeditor = Karten Bearbeiter
|
||||||
|
text.donate = Spenden
|
||||||
|
text.settings.reset = Auf Standard zurücksetzen
|
||||||
|
text.settings.controls = Steuerung
|
||||||
|
text.settings.game = Spiel
|
||||||
|
text.settings.sound = Audio
|
||||||
|
text.settings.graphics = Grafiken
|
||||||
|
text.upgrades = Verbesserungen
|
||||||
|
text.purchased = [LIME] Erstellt!
|
||||||
|
text.weapons = Waffen
|
||||||
|
text.paused = Pausiert
|
||||||
|
text.respawn = Respawn in
|
||||||
|
text.error.title = [crimson] Ein Fehler ist aufgetreten
|
||||||
|
text.error.crashmessage = [SCARLET] Es ist ein unerwarteter Fehler aufgetreten, der einen Absturz verursacht hätte. [] Bitte geben Sie die genauen Umstände an, unter denen dieser Fehler passiert ist, für den Entwickler: [ORANGE] anukendev@gmail.com []
|
||||||
|
text.error.crashtitle = EIn Fehler ist aufgetreten!
|
||||||
|
text.mode.break = Zerstörungsmodus: {0}
|
||||||
|
text.mode.place = Platzierungsmodus: {0}
|
||||||
|
placemode.hold.name = Zeile
|
||||||
|
placemode.areadelete.name = Gebiet
|
||||||
|
placemode.touchdelete.name = berühren
|
||||||
|
placemode.holddelete.name = halten
|
||||||
|
placemode.none.name = keine
|
||||||
|
placemode.touch.name = berühren
|
||||||
|
placemode.cursor.name = Mauszeiger
|
||||||
|
text.blocks.extrainfo = [accent] Extra Blockinfo:
|
||||||
|
text.blocks.blockinfo = Blockinfo:
|
||||||
|
text.blocks.powercapacity = Energiekapazität
|
||||||
|
text.blocks.powershot = Energie / Schuss
|
||||||
|
text.blocks.powersecond = Energie / Sekunde
|
||||||
|
text.blocks.powerdraindamage = Energieabnahme / Schaden
|
||||||
|
text.blocks.shieldradius = Schildradius
|
||||||
|
text.blocks.itemspeedsecond = Gegenstands Geschwindigkeit / Sekunde
|
||||||
|
text.blocks.range = Reichweite
|
||||||
|
text.blocks.size = Grösse
|
||||||
|
text.blocks.powerliquid = Energie / Flüssigkeit
|
||||||
|
text.blocks.maxliquidsecond = Max Flüssigkeit / Sekunde
|
||||||
|
text.blocks.liquidcapacity = Flüssigkeitskapazität
|
||||||
|
text.blocks.liquidsecond = Flüssigkeit / Sekunde
|
||||||
|
text.blocks.damageshot = Schaden / Schuss
|
||||||
|
text.blocks.ammocapacity = Munitionskapazität
|
||||||
|
text.blocks.ammo = Munition
|
||||||
|
text.blocks.ammoitem = Munition / Gegenstand
|
||||||
|
text.blocks.maxitemssecond = Max Gegenstände / Sekunde
|
||||||
|
text.blocks.powerrange = Energiereichweite
|
||||||
|
text.blocks.lasertilerange = Laser Reichweite
|
||||||
|
text.blocks.capacity = Kapazität
|
||||||
|
text.blocks.itemcapacity = Gegenstand Kapazität
|
||||||
|
text.blocks.maxpowergenerationsecond = Max Energieerzeugung / Sekunde
|
||||||
|
text.blocks.powergenerationsecond = Energieerzeugung / Sekunde
|
||||||
|
text.blocks.generationsecondsitem = Generation Sekunden / Gegenstand
|
||||||
|
text.blocks.input = Eingabe
|
||||||
|
text.blocks.inputliquid = Flüssigkeiten Eingabe
|
||||||
|
text.blocks.inputitem = Eingabe Gegenstand
|
||||||
|
text.blocks.output = Ausgabe
|
||||||
|
text.blocks.secondsitem = Sekunden / Item
|
||||||
|
text.blocks.maxpowertransfersecond = Max Energieübertragung / Sekunde
|
||||||
|
text.blocks.explosive = Hochexplosiv!
|
||||||
|
text.blocks.repairssecond = Reparaturen / Sekunde
|
||||||
|
text.blocks.health = Lebenspunkte
|
||||||
|
text.blocks.inaccuracy = Ungenauigkeit
|
||||||
|
text.blocks.shots = Schüsse
|
||||||
|
text.blocks.shotssecond = Schüsse / Sekunde
|
||||||
|
text.blocks.fuel = Treibstoff
|
||||||
|
text.blocks.fuelduration = Treibstoffdauer
|
||||||
|
text.blocks.maxoutputsecond = Max Ausgabe / Sekunde
|
||||||
|
text.blocks.inputcapacity = Eingabekapazität
|
||||||
|
text.blocks.outputcapacity = Ausgabekapazität
|
||||||
|
text.blocks.poweritem = Energie / Gegenstand
|
||||||
|
text.placemode = Platzierungsmodus
|
||||||
|
text.breakmode = Zerstörungsmodus
|
||||||
|
text.health = Lebenspunkte
|
||||||
|
setting.difficulty.easy = Leicht
|
||||||
|
setting.difficulty.normal = Normal
|
||||||
|
setting.difficulty.hard = Schwer
|
||||||
|
setting.difficulty.insane = Unmöglich
|
||||||
|
setting.difficulty.purge = Auslöschung
|
||||||
|
setting.difficulty.name = Schwierigkeit
|
||||||
|
setting.screenshake.name = Bildschirm wackeln
|
||||||
|
setting.smoothcam.name = Glatte Kamera
|
||||||
|
setting.indicators.name = Feind Indikatoren
|
||||||
|
setting.effects.name = Effekte anzeigen
|
||||||
|
setting.sensitivity.name = Kontroller Empfindlichkeit
|
||||||
|
setting.saveinterval.name = Autosave Häufigkeit
|
||||||
|
setting.seconds = {0} Sekunden
|
||||||
|
setting.fps.name = Zeige FPS
|
||||||
|
setting.vsync.name = VSync
|
||||||
|
setting.lasers.name = Zeige Energielaser
|
||||||
|
setting.healthbars.name = Zeige Objekt Lebensbalken
|
||||||
|
setting.pixelate.name = Pixel Bildschirm
|
||||||
|
setting.musicvol.name = Musiklautstärke
|
||||||
|
setting.mutemusic.name = Musik stummschalten
|
||||||
|
setting.sfxvol.name = Audioeffekte Lautstärke
|
||||||
|
setting.mutesound.name = Audioeffekte stummschalten
|
||||||
|
map.maze.name = Labyrinth
|
||||||
|
map.fortress.name = Festung
|
||||||
|
map.sinkhole.name = Sinkloch
|
||||||
|
map.caves.name = Höhlen
|
||||||
|
map.volcano.name = Vulkan
|
||||||
|
map.caldera.name = Lavakessel
|
||||||
|
map.scorch.name = Flammen
|
||||||
|
map.desert.name = Wüste
|
||||||
|
map.island.name = Insel
|
||||||
|
map.grassland.name = Grasland
|
||||||
|
map.tundra.name = Kältesteppe
|
||||||
|
map.spiral.name = Spirale
|
||||||
|
map.tutorial.name = Tutorial
|
||||||
|
tutorial.intro.text = [gelb] Willkommen zum Tutorial [] Um zu beginnen, drücke 'weiter'.
|
||||||
|
tutorial.moveDesktop.text = Verwende zum Verschieben die Tasten [orange] [[WASD] []. Halte [orange] Shift [] gedrückt, um zu erhöhen. Halte [orange] CTRL/STRG [] gedrückt, während du das [orange] Scrollrad [] zum Vergrössern oder Verkleinern verwendest.
|
||||||
|
tutorial.shoot.text = Ziele mit der Maus, halte die [orange] linke Maustaste [] gedrückt, um zu schiessen. Versuche es mit dem [gelben] Ziel [].
|
||||||
|
tutorial.moveAndroid.text = Um die Ansicht zu verschieben, ziehe einen Finger über den Bildschirm. Drücke und ziehe, um zu vergrössern oder zu verkleinern.
|
||||||
|
tutorial.placeSelect.text = Versuche, ein [gelbes] Förderband [] aus dem Blockmenü unten rechts auszuwählen.
|
||||||
|
tutorial.placeConveyorDesktop.text = Verwende das [orange] [[scrollwheel] [], um das Förderband nach vorne zu bewegen [orange] vorwärts [], und platziere es dann an der [gelben] markierten Stelle [] mit [orange] [[linke Maustaste] [].
|
||||||
|
tutorial.placeConveyorAndroid.text = Verwende die [orange] [[rotieren-Taste] [], um das Förderband nach vorne [orange] zu drehen [], ziehe es mit einem Finger in Position und platziere es dann an der [gelben] markierten Stelle [] mit der [Orange] [[Häkchen][].
|
||||||
|
tutorial.placeConveyorAndroidInfo.text = Alternativ kannst du das Fadenkreuzsymbol unten links drücken, um zum [orange] [[touch mode] [] zu wechseln, und Blöcke durch Tippen auf den Bildschirm platzieren. Im Touch-Modus können Blöcke mit dem Pfeil unten links gedreht werden. Drücke [gelb] neben [], um es auszuprobieren.
|
||||||
|
tutorial.placeDrill.text = Wähle nun einen [gelben] Steinbohrer [] an der markierten Stelle aus und platziere ihn.
|
||||||
|
tutorial.blockInfo.text = Wenn du mehr über einen Block erfahren möchtest, tippe oben rechts auf das [orange] Fragezeichen [], um dessen Beschreibung zu lesen.
|
||||||
|
tutorial.deselectDesktop.text = Du kannst einen Block mit [Orange] [[Rechte Maustaste] [] abwählen.
|
||||||
|
tutorial.deselectAndroid.text = Du kannst einen Block abwählen, indem du die [orange] X [] -Taste drücken.
|
||||||
|
tutorial.drillPlaced.text = Der Bohrer erzeugt nun [gelben] Stein, [] gib den Stein nun auf das Förderband aus und bewege ihn dann in den [gelben] Kern [].
|
||||||
|
tutorial.drillInfo.text = Verschiedene Erze benötigen unterschiedliche Bohrer. Stein erfordert Steinbohrer, Eisen erfordert Eisenbohrer usw.
|
||||||
|
tutorial.drillPlaced2.text = Wenn du Gegenstände in den Kern verschiebst, steckst du sie in dein [gelbes] Inventar [] oben links. Das Platzieren von Blöcken verwendet Gegenstände aus deinem Inventar.
|
||||||
|
tutorial.moreDrills.text = Du kannst so viele Bohrer und Förderer miteinander verbinden wie du lust hast.
|
||||||
|
tutorial.deleteBlock.text = Du kannst Blöcke löschen, indem du mit der [orange] rechte Maustaste [] auf dem Block klickst, den du löschen möchtest. Versuche, dieses Förderband zu löschen.
|
||||||
|
tutorial.deleteBlockAndroid.text = Du kannst Blöcke löschen, indem du [orange] das Fadenkreuz [] im [orange] Pausenmodusmenü [] links unten auswählst und auf einen Block tippst. Versuche, dieses Fliessband zu löschen.
|
||||||
|
tutorial.placeTurret.text = Wähle nun einen [gelben] Turm [] an der [gelben] markierten Stelle [] und platziere ihn.
|
||||||
|
tutorial.placedTurretAmmo.text = Dieser Turm nimmt nun [gelbe] Munition [] vom Förderband an. Du kannst sehen, wie viel Munition es hat, indem du darüber schweben und den [grünen] grünen Balken [] prüfen.
|
||||||
|
tutorial.turretExplanation.text = Geschütze schiessen automatisch auf den nächsten Feind in Reichweite, solange sie genug Munition haben.
|
||||||
|
tutorial.waves.text = Jede [yellow] 60 [] Sekunden wird eine Welle von [coral] Feinden [] an einem bestimmten Orten erscheinen und versuchen, den Kern zu zerstören.
|
||||||
|
tutorial.coreDestruction.text = Dein Ziel ist es, den Kern [yellow] zu verteidigen. Wenn der Kern zerstört wird, verlierst du [coral] das Spiel [].
|
||||||
|
tutorial.pausingDesktop.text = Wenn du jemals eine Pause machen möchtest, drücke die [orange] Pause-Taste [] oben links oder [orange]space[], um das Spiel anzuhalten. Du kannst auch Blöcke immer noch auswählen und platzieren, während du das Spiel pausiert ist, aber Sie können sich nicht bewegen oder schiessen.
|
||||||
|
tutorial.pausingAndroid.text = Wenn du jemals eine Pause machen willst, drück einfach die [orange] Pause-Taste [] oben links, um das Spiel anzuhalten. Sie können immer noch Sachen auswählen und Blöcke während der Pause platzieren.
|
||||||
|
tutorial.purchaseWeapons.text = Du kannst neue [yellow] Waffen [] für deinen Mech kaufen, indem du das Verbesserungs-Menü unten links öffnest.
|
||||||
|
tutorial.switchWeapons.text = Schalte Waffen, indem du entweder auf das Symbol unten links klickst oder Nummern verwendest [orange] [[1-9] [].
|
||||||
|
tutorial.spawnWave.text = Hier kommt die erste Welle. Zerstöre sie.
|
||||||
|
tutorial.pumpDesc.text = In späteren Wellen müsst du möglicherweise [yellow] Pumpen [] verwenden, um Flüssigkeiten für Generatoren oder Extraktoren zu verteilen.
|
||||||
|
tutorial.pumpPlace.text = Pumpen arbeiten ähnlich wie Bohrer, ausser dass sie anstelle von Gegenständen Flüssigkeiten produzieren. Versuch mal, eine Pumpe auf das [yellow] gekennzeichnete Öl [] zu setzen.
|
||||||
|
tutorial.conduitUse.text = Stellen Sie nun eine [orange] Leitungsrohr [] von der Pumpe weg.
|
||||||
|
tutorial.conduitUse2.text = Und noch ein paar mehr ...
|
||||||
|
tutorial.conduitUse3.text = Und noch ein paar mehr ...
|
||||||
|
tutorial.generator.text = Stellen Sie nun einen [orange] Verbrennungsgenerator [] Block am Ende des Leitungsrohres auf.
|
||||||
|
tutorial.generatorExplain.text = Dieser Generator erzeugt nun [yellow] Energie [] aus dem Öl.
|
||||||
|
tutorial.lasers.text = Die Energie wird mit [yellow] Energielasern [] verteilt. Drehe und platziere einen hier.
|
||||||
|
tutorial.laserExplain.text = Der Generator wird nun Energie in den Laserblock bewegen. Ein [yellow] undurchsichtiger [] Strahl bedeutet, dass er gerade Leistung überträgt, und ein [yellow] transparenter [] Strahl bedeutet, dass dies nicht der Fall ist.
|
||||||
|
tutorial.laserMore.text = Du kannst überprüfen, wie viel Energie ein Block hat, indem du darüber schweben und den [yellow] gelben Balken [] oben prüfen.
|
||||||
|
tutorial.healingTurret.text = Dieser Laser kann verwendet werden, um einen [lime] -Reparaturgeschütz [] anzutreiben. Platziere einen hier.
|
||||||
|
tutorial.healingTurretExplain.text = Solange er Kraft hat, repariert dieser Turm in der Nähe Blöcke. [] Wenn du spielst, stelle sicher, dass du so schnell wie möglich einen in deiner Basis bekommst!
|
||||||
|
tutorial.smeltery.text = Viele Blöcke benötigen [orange] Stahl [], um eine [orange] Schmelzer [] herzustellen. Platziere einen hier.
|
||||||
|
tutorial.smelterySetup.text = Diese Schmelzer wird nun [orange] Stahl [] aus dem Eingangs-Eisen produzieren, wobei Kohle als Brennstoff verwendet wird.
|
||||||
|
tutorial.end.text = Und damit ist das Tutorial abgeschlossen! Viel Glück!
|
||||||
|
keybind.move_x.name = bewege_x
|
||||||
|
keybind.move_y.name = bewege_y
|
||||||
|
keybind.select.name = wählen
|
||||||
|
keybind.break.name = Unterbrechung
|
||||||
|
keybind.shoot.name = Schiess
|
||||||
|
keybind.zoom_hold.name = zoomen_halten
|
||||||
|
keybind.zoom.name = zoomen
|
||||||
|
keybind.menu.name = Menü
|
||||||
|
keybind.pause.name = Pause
|
||||||
|
keybind.dash.name = Bindestrich
|
||||||
|
keybind.rotate_alt.name = drehen_alt
|
||||||
|
keybind.rotate.name = Drehen
|
||||||
|
keybind.weapon_1.name = Waffe_1
|
||||||
|
keybind.weapon_2.name = Waffe_2
|
||||||
|
keybind.weapon_3.name = Waffe_3
|
||||||
|
keybind.weapon_4.name = Waffe_4
|
||||||
|
keybind.weapon_5.name = Waffe_5
|
||||||
|
keybind.weapon_6.name = Waffe_6
|
||||||
|
mode.waves.name = Wellen
|
||||||
|
mode.sandbox.name = Sandkasten
|
||||||
|
mode.freebuild.name = Freier Bau
|
||||||
|
upgrade.standard.name = Standard
|
||||||
|
upgrade.standard.description = Der Standardmech.
|
||||||
|
upgrade.blaster.name = Blaster
|
||||||
|
upgrade.blaster.description = Schiesst eine langsame, schwache Kugel.
|
||||||
|
upgrade.triblaster.name = Dreifach-Blaster
|
||||||
|
upgrade.triblaster.description = Schiesst 3 Kugeln in einer Ausbreitung.
|
||||||
|
upgrade.clustergun.name = Klumpen-Waffe
|
||||||
|
upgrade.clustergun.description = Schiesst eine ungenaue Verbreitung von explosiven Granaten.
|
||||||
|
upgrade.beam.name = Strahlkanone
|
||||||
|
upgrade.beam.description = Schiesst einen weitreichenden durchdringenden Laserstrahl.
|
||||||
|
upgrade.vulcan.name = Vulkan
|
||||||
|
upgrade.vulcan.description = Schiesst eine Flut von schnellen Kugeln.
|
||||||
|
upgrade.shockgun.name = Schock-Waffe
|
||||||
|
upgrade.shockgun.description = Erschiesst eine verheerende Explosion von geladenen Granatsplittern.
|
||||||
|
item.stone.name = Stein
|
||||||
|
item.iron.name = Eisen
|
||||||
|
item.coal.name = Kohle
|
||||||
|
item.steel.name = Stahl
|
||||||
|
item.titanium.name = Titan
|
||||||
|
item.dirium.name = Dirium
|
||||||
|
item.uranium.name = Uran
|
||||||
|
item.sand.name = Sand
|
||||||
|
liquid.water.name = Wasser
|
||||||
|
liquid.plasma.name = Plasma
|
||||||
|
liquid.lava.name = Lava
|
||||||
|
liquid.oil.name = Öl
|
||||||
|
block.weaponfactory.name = Waffenfabrik
|
||||||
|
block.air.name = Luft
|
||||||
|
block.blockpart.name = Blockteil
|
||||||
|
block.deepwater.name = tiefes Wasser
|
||||||
|
block.water.name = Wasser
|
||||||
|
block.lava.name = Lava
|
||||||
|
block.oil.name = Öl
|
||||||
|
block.stone.name = Stein
|
||||||
|
block.blackstone.name = schwarzer Stein
|
||||||
|
block.iron.name = Eisen
|
||||||
|
block.coal.name = Kohle
|
||||||
|
block.titanium.name = Titan
|
||||||
|
block.uranium.name = Uran
|
||||||
|
block.dirt.name = Erde
|
||||||
|
block.sand.name = Sand
|
||||||
|
block.ice.name = Eis
|
||||||
|
block.snow.name = Schnee
|
||||||
|
block.grass.name = Gras
|
||||||
|
block.sandblock.name = Sandstein
|
||||||
|
block.snowblock.name = Schneeblock
|
||||||
|
block.stoneblock.name = Steinblock
|
||||||
|
block.blackstoneblock.name = Schwarzer Stein
|
||||||
|
block.grassblock.name = Grasblock
|
||||||
|
block.mossblock.name = Moosblock
|
||||||
|
block.shrub.name = Busch
|
||||||
|
block.rock.name = Felsen
|
||||||
|
block.icerock.name = Eisfelsen
|
||||||
|
block.blackrock.name = Schwarzer Felsen
|
||||||
|
block.dirtblock.name = Erdblock
|
||||||
|
block.stonewall.name = Steinwand
|
||||||
|
block.stonewall.fulldescription = Ein billiger Verteidigungsblock. Nützlich zum Schutz des Kerns und der Geschütztürme in den ersten Wellen.
|
||||||
|
block.ironwall.name = Eisenwand
|
||||||
|
block.ironwall.fulldescription = Ein grundlegender Verteidigungsblock. Bietet Schutz vor Feinden.
|
||||||
|
block.steelwall.name = Stahlwand
|
||||||
|
block.steelwall.fulldescription = Ein Standard-Verteidigungsblock. Bietet angemessen Schutz vor Feinden.
|
||||||
|
block.titaniumwall.name = Titanwand
|
||||||
|
block.titaniumwall.fulldescription = Eine starke Abwehrblockade. Bietet Schutz vor Feinden.
|
||||||
|
block.duriumwall.name = Diriumwand
|
||||||
|
block.duriumwall.fulldescription = Eine sehr starke Abwehrblockade. Bietet guten Schutz vor Feinden.
|
||||||
|
block.compositewall.name = Verbundende Wand
|
||||||
|
block.steelwall-large.name = grosse Stahlwand
|
||||||
|
block.steelwall-large.fulldescription = Ein Standard-Verteidigungsblock. Mehrere Blöcke gross.
|
||||||
|
block.titaniumwall-large.name = grosse Titanwand
|
||||||
|
block.titaniumwall-large.fulldescription = Eine starke Abwehrblockade. Mehrere Blöcke gross.
|
||||||
|
block.duriumwall-large.name = grosse Diriumwand
|
||||||
|
block.duriumwall-large.fulldescription = Eine sehr starke Abwehrblockade. Mehrere Blöcke gross.
|
||||||
|
block.titaniumshieldwall.name = geschützte Wand
|
||||||
|
block.titaniumshieldwall.fulldescription = Ein starker Abwehrblock mit einem extra eingebauten Schild. Benötigt Energie. Verwendet Energie, um feindliche Kugeln zu absorbieren. Es wird empfohlen, Verstärker zu verwenden, um diesem Block Energie zuzuführen.
|
||||||
|
block.repairturret.name = Reparaturgeschütz
|
||||||
|
block.repairturret.fulldescription = Repariert beschädigte Blöcke in der Nähe in einem langsamen Tempo. Nutzt geringe Mengen an Energie.
|
||||||
|
block.megarepairturret.name = Reparaturgeschütz II
|
||||||
|
block.megarepairturret.fulldescription = Repariert in der Nähe beschädigte Blöcke in Reichweite zu einem vernünftigen Preis. Verwendet Energie.
|
||||||
|
block.shieldgenerator.name = Schildgenerator
|
||||||
|
block.shieldgenerator.fulldescription = Ein fortgeschrittener Verteidigungsblock. Schützt alle Blöcke in einem Radius vor Angriffen. Bei keinem Betrieb langsamer Strom verbrauch, verliert bei Kugelkontakt jedoch schnell Energie.
|
||||||
|
block.door.name = Tür
|
||||||
|
block.door.fulldescription = Ein Block, der durch Antippen geöffnet und geschlossen werden kann.
|
||||||
|
block.door-large.name = grosse Tür
|
||||||
|
block.door-large.fulldescription = Ein Block der mehrere Felder gross ist und der durch Antippen geöffnet und geschlossen werden kann.
|
||||||
|
block.conduit.name = Leitungsrohr
|
||||||
|
block.conduit.fulldescription = Grundlegender Flüssigkeitstransportblock. Funktioniert wie ein Förderband, aber mit Flüssigkeiten. Am besten mit Pumpen oder anderen Leitungen verwenden. Kann als Brücke für Gegner und Spieler verwendet werden.
|
||||||
|
block.pulseconduit.name = Pulsleitungsrohr
|
||||||
|
block.pulseconduit.fulldescription = Fortschrittlicher Flüssigkeitstransportblock. Transportiert Flüssigkeiten schneller und speichert mehr als normale Leitungsrohre.
|
||||||
|
block.liquidrouter.name = flüssigkeiten verteiler
|
||||||
|
block.liquidrouter.fulldescription = Funktioniert ähnlich wie ein Router. Akzeptiert Flüssigkeit von einer Seite und gibt sie auf die anderen Seiten aus. Nützlich zum Aufspalten von Flüssigkeit aus eines einzelnen Leitungsrohres in mehrere andere Leitungensrohre.
|
||||||
|
block.conveyor.name = Förderband
|
||||||
|
block.conveyor.fulldescription = Grundelement Transport Block. Bewegt Gegenstände nach vorne und legt sie automatisch in Türmen oder ähnliches. Drehbar. Kann als Brücke für Gegner und Spieler verwendet werden.
|
||||||
|
block.steelconveyor.name = Stahlförderband
|
||||||
|
block.steelconveyor.fulldescription = Erweiterter Transportblock Bewegt Gegenstände schneller als Standardförderer.
|
||||||
|
block.poweredconveyor.name = Impulsförderband
|
||||||
|
block.poweredconveyor.fulldescription = Der ultimative Transportblock für Gegenstände. Bewegt Gegenstände noch schneller als Stahlförderer.
|
||||||
|
block.router.name = Verteiler
|
||||||
|
block.router.fulldescription = Akzeptiert Objekte aus einer Richtung und gibt sie in 3 andere Richtungen aus. Kann auch eine bestimmte Anzahl von Gegenständen speichern. Geeignet zum Aufteilen der Materialien von einem Bohrer in mehrere Geschütze.
|
||||||
|
block.junction.name = Kreuzung
|
||||||
|
block.junction.fulldescription = Fungiert als Brücke für zwei kreuzende Förderbänder. Nützlich in Situationen mit zwei verschiedenen Förderbänder, die unterschiedliche Materialien zu verschiedenen Orten transportieren.
|
||||||
|
block.conveyortunnel.name = Förderbandtunnel
|
||||||
|
block.conveyortunnel.fulldescription = Transportiert Artikel unter Blöcken. Verwendung für einen Tunnel, der in den zu untertunnelnden Block führt, und einen auf der anderen Seite. Stellen Sie sicher, dass beide Tunnel in entgegengesetzte Richtungen weisen, das heisst das die Tunnel voneinander weggucken.
|
||||||
|
block.liquidjunction.name = flüssigkeite Kreuzung
|
||||||
|
block.liquidjunction.fulldescription = Funktioniert als Brücke für zwei kreuzende Leitungsrohre. Nützlich in Situationen mit zwei verschiedenen Leitungen, die unterschiedliche Flüssigkeiten zu verschiedenen Orten transportieren.
|
||||||
|
block.liquiditemjunction.name = Flüssigkeit-Gegenstand-Kreuzung
|
||||||
|
block.liquiditemjunction.fulldescription = Fungiert als Brücke zum Überqueren von Leitungsrohre und Förderbändern.
|
||||||
|
block.powerbooster.name = Energieverstärker
|
||||||
|
block.powerbooster.fulldescription = Verteilt die Energie an alle Blöcke innerhalb seines Radius.
|
||||||
|
block.powerlaser.name = Energielaser
|
||||||
|
block.powerlaser.fulldescription = Erzeugt einen Laser, der die Kraft auf den Block davor überträgt. Erzeugt selbst keine Energie. Am besten mit Generatoren oder anderen Lasern verwendet.
|
||||||
|
block.powerlaserrouter.name = Laser Verteiler
|
||||||
|
block.powerlaserrouter.fulldescription = Laser, der die Kraft in drei Richtungen gleichzeitig verteilt. Nützlich in Situationen, in denen mehrere Blöcke von einem Generator mit Strom versorgt werden müssen.
|
||||||
|
block.powerlasercorner.name = Laser-Ecke
|
||||||
|
block.powerlasercorner.fulldescription = Laser, der die Kraft in zwei Richtungen gleichzeitig verteilt. Nützlich in Situationen, in denen mehrere Blöcke von einem Generator mit Strom versorgt werden müssen und ein Router ungenau ist.
|
||||||
|
block.teleporter.name = Teleporter
|
||||||
|
block.teleporter.fulldescription = Erweiterter Transportblock Teleporter geben Gegenstände in andere Teleporter derselben Farbe ein. Tut nichts, wenn keine Teleporter derselben Farbe existieren. Wenn mehrere Teleporter mit derselben Farbe existieren, wird eine zufällige ausgewählt. Verwendet Energie. Tippen, um die Farbe zu ändern.
|
||||||
|
block.sorter.name = Sortierer
|
||||||
|
block.sorter.fulldescription = Sortiert den Gegenstand nach Materialart. Das zu akzeptierende Material wird durch die Farbe im Block angezeigt. Alle Artikel, die dem Sortiermaterial entsprechen, werden vorwärts ausgegeben, alles andere wird nach links und rechts ausgegeben.
|
||||||
|
block.core.name = Kern
|
||||||
|
block.pump.name = Pumpe
|
||||||
|
block.pump.fulldescription = Pumpen Flüssigkeiten aus einem Quellblock - meist Wasser, Lava oder Öl. Gibt Flüssigkeit in benachbarte Leitungsrohre aus.
|
||||||
|
block.fluxpump.name = flux Pumpe
|
||||||
|
block.fluxpump.fulldescription = Eine erweiterte Version der Pumpe. Speichert mehr Flüssigkeit und pumpt Flüssigkeit schneller.
|
||||||
|
block.smelter.name = Schmelzer
|
||||||
|
block.smelter.fulldescription = Der essentielle Bastelblock. Wenn 1x Eisen und 1x Kohle eingegeben wird, wird 1x Stahl ausgegeben.
|
||||||
|
block.crucible.name = Tiegel
|
||||||
|
block.crucible.fulldescription = Ein fortgeschrittener Handwerksblock. Braucht Kohle um zu funktionieren. Wenn 1x Titan und 1x Stahl eingegeben wird, wird 1x Dirium ausgegeben.
|
||||||
|
block.coalpurifier.name = Kohle-Extraktor
|
||||||
|
block.coalpurifier.fulldescription = Ein einfacher Extraktorblock. Gibt Kohle aus, wenn der Block mit grossen Mengen Wasser und Stein gefüllt wird.
|
||||||
|
block.titaniumpurifier.name = Titan-Extraktor
|
||||||
|
block.titaniumpurifier.fulldescription = Ein Standard-Extraktorblock. Gibt Titan aus wenn er mit grossen Mengen Wasser und Eisen gefüllt wird.
|
||||||
|
block.oilrefinery.name = Ölraffinerie
|
||||||
|
block.oilrefinery.fulldescription = Veredelt grosse Mengen Öl zu Kohle. Nützlich für die Betankung von Blöcken die Kohle benutzen wie z.b. Waffentürme, wenn Kohleadern knapp sind.
|
||||||
|
block.stoneformer.name = Steinformer
|
||||||
|
block.stoneformer.fulldescription = Verfestigt flüssige Lava zu Stein. Nützlich für die Herstellung von grossen Mengen von Stein für Kohle-Extraktor.
|
||||||
|
block.lavasmelter.name = Lava-Schmelzer
|
||||||
|
block.lavasmelter.fulldescription = Verwendet Lava, um Eisen zu Stahl schmelzen. Eine Alternative zum Schmelzer. Nützlich in Situationen, in denen Kohle knapp ist.
|
||||||
|
block.stonedrill.name = Steinbohrer
|
||||||
|
block.stonedrill.fulldescription = Der wesentliche Bohrer. Wenn er auf Steinplatten gelegt wird, gibt er Steine mit einer langsamen Geschwindigkeit auf endlosen Zeit aus.
|
||||||
|
block.irondrill.name = Eisenbohrer
|
||||||
|
block.irondrill.fulldescription = Eine grundlegender Bohrer. Wenn es auf Eisenerz gelegt wird, gibt er Eisen auf endloser Zeit langsam aus.
|
||||||
|
block.coaldrill.name = Kohlenbohrer
|
||||||
|
block.coaldrill.fulldescription = Eine grundlegender Bohrer. Wenn es auf Kohleerz platziert wird, gibt er für endloser Zeit langsam Kohle aus.
|
||||||
|
block.uraniumdrill.name = Uran-Bohrer
|
||||||
|
block.uraniumdrill.fulldescription = Ein fortgeschrittener Bohrer. Wenn es auf Uranerz platziert wird, gibt er Uran mit einer langsamen Geschwindigkeit auf endloser Zeit ab.
|
||||||
|
block.titaniumdrill.name = Titan-Bohrer
|
||||||
|
block.titaniumdrill.fulldescription = Ein fortgeschrittener Bohrer. Wenn es auf Titanerz platziert wird, gibt er Titan langsam aus für undendlich langer Zeit
|
||||||
|
block.omnidrill.name = Omni-Bohrer
|
||||||
|
block.omnidrill.fulldescription = Der ultimative Bohrer. Baut in schnellem Tempo jegliches Erz ab.
|
||||||
|
block.coalgenerator.name = Kohle-Generator
|
||||||
|
block.coalgenerator.fulldescription = Der wesentliche Generator. Erzeugt Energie aus Kohle. Gibt Energie als Laser an seine 4 Seiten aus.
|
||||||
|
block.thermalgenerator.name = thermischer Generator
|
||||||
|
block.thermalgenerator.fulldescription = Erzeugt Energie aus Lava. Gibt Energie als Laser an seine 4 Seiten aus.
|
||||||
|
block.combustiongenerator.name = Verbrennungsgenerator
|
||||||
|
block.combustiongenerator.fulldescription = Erzeugt Energie aus Öl. Gibt Energie als Laser an seine 4 Seiten aus.
|
||||||
|
block.rtgenerator.name = RTG-Generator
|
||||||
|
block.rtgenerator.fulldescription = Erzeugt geringe Mengen an Energie aus dem radioaktiven Zerfall von Uran. Gibt Energie als Laser an seine 4 Seiten aus.
|
||||||
|
block.nuclearreactor.name = Kernreaktor
|
||||||
|
block.nuclearreactor.fulldescription = Eine erweiterte Version des RTG-Generators und der ultimative Energie Generator. Erzeugt Strom aus Uran. Erfordert konstante Wasserkühlung. Sehr heiss; explodiert heftig, wenn zu wenig Kühlmittel zugeführt wird.
|
||||||
|
block.turret.name = Geschütz
|
||||||
|
block.turret.fulldescription = Ein einfacher, billiger Turm. Verwendet Stein für Munition. Hat etwas mehr Reichweite als das Doppelgeschütz.
|
||||||
|
block.doubleturret.name = Doppelgeschütz
|
||||||
|
block.doubleturret.fulldescription = Eine etwas stärkere Version des Geschützes. Verwendet Stein für Munition. Hat deutlich mehr Schaden, hat aber eine geringere Reichweite. Schiesst zwei Kugeln.
|
||||||
|
block.machineturret.name = Gatling Geschütz
|
||||||
|
block.machineturret.fulldescription = Ein Standard-Allround-Turm. Verwendet Eisen für Munition. Hat eine schnelle Feuerrate mit gutem Schaden.
|
||||||
|
block.shotgunturret.name = Splittergeschütz
|
||||||
|
block.shotgunturret.fulldescription = Ein Standard-Turm. Verwendet Eisen für Munition. Schiesst 7 Kugeln auf einmal. Geringere Reichweite, aber höhere Schadensleistung als das Gatling Geschütz
|
||||||
|
block.flameturret.name = Flammenwerfer
|
||||||
|
block.flameturret.fulldescription = Fortschrittlicher Nahbereichswaffe. Verwendet Kohle für Munition. Hat eine sehr geringe Reichweite, aber sehr hohen Schaden. Gut für Nahkampf. Empfohlen für den Einsatz hinter Mauern.
|
||||||
|
block.sniperturret.name = Schienenkanone
|
||||||
|
block.sniperturret.fulldescription = Fortschrittliches Reichweitengeschütz. Verwendet Stahl für Munition. Sehr hoher Schaden, aber niedrige Feuerrate. Teuer zu verwenden, kann aber aufgrund seiner Reichweite weit entfernt von den feindlichen Linien platziert werden.
|
||||||
|
block.mortarturret.name = Flakgeschütz
|
||||||
|
block.mortarturret.fulldescription = Fortschrittlicher Flächen-Schaden Turm. Verwendet Kohle für Munition. Sehr langsame Feuerrate und Geschosse, aber sehr hoher Einzelziel- und Flächenschaden. Nützlich gegen grosse Mengen von Feinden.
|
||||||
|
block.laserturret.name = Laserturm
|
||||||
|
block.laserturret.fulldescription = Fortschrittlicher Einzelziel-Turm. Verwendet Strom. Guter Allround-Revolver für mittlere Reichweiten. Nur Einzelziel. Verfehlt nie.
|
||||||
|
block.waveturret.name = Teslakanone
|
||||||
|
block.waveturret.fulldescription = Erweiterter Mehrfach-Ziele-Turm. Verwendet Strom. Mittlere Reichweite. Verfehlt nie. Im Durchschnitt zu wenig Schaden, aber kann mehrere Feinde gleichzeitig mit Kettenblitz treffen.
|
||||||
|
block.plasmaturret.name = Plasmageschütz
|
||||||
|
block.plasmaturret.fulldescription = Hochentwickelte Version des Flammenwerfers. Verwendet Kohle als Munition. Sehr hoher Schaden, niedriger bis mittlerer Reichweite.
|
||||||
|
block.chainturret.name = Kettengeschütz
|
||||||
|
block.chainturret.fulldescription = Die ultimative Schnellfeuer Waffe. Verwendet Uran als Munition. Schiesst grosse Kugeln mit hoher Feuerrate. Mittlere Reichweite. Mehrere Felder gross. Hält extrem viel aus.
|
||||||
|
block.titancannon.name = Titan Kanone
|
||||||
|
block.titancannon.fulldescription = Die ultimative Langstrecken Kanone. Verwendet Uran als Munition. Schiesst grosse Flächen-Schadenden-Granaten mit einer mittleren Feuerrate ab. Lange Reichweite. Ist mehrere Felder gross. Hält extrem viel aus.
|
||||||
|
block.playerspawn.name = Spielerspawn
|
||||||
|
block.enemyspawn.name = Gegnerspawn
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 81 KiB |
@@ -62,7 +62,7 @@ io.anuke.ucore.scene.ui.ImageButton$ImageButtonStyle: {
|
|||||||
emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: lightgray},
|
emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: lightgray},
|
||||||
static: {up: button },
|
static: {up: button },
|
||||||
static-down: {up: button-down },
|
static-down: {up: button-down },
|
||||||
toggle: {checked: button-down, down: button-down, up: button },
|
toggle: {checked: button-down, down: button-down, up: button, imageDisabledColor: lightgray, imageUpColor: white },
|
||||||
togglemap: {down: button-map-down, up: button-map },
|
togglemap: {down: button-map-down, up: button-map },
|
||||||
select: {checked: button-select, up: clear },
|
select: {checked: button-select, up: clear },
|
||||||
close-window: {up: button, imageUp: icon-close, imageOver: icon-close-over, imageDown: icon-close-down, disabled: button }
|
close-window: {up: button, imageUp: icon-close, imageOver: icon-close-over, imageDown: icon-close-down, disabled: button }
|
||||||
@@ -83,7 +83,7 @@ io.anuke.ucore.scene.ui.Window$WindowStyle: {
|
|||||||
dialog: {stageBackground: dialogDim, titleFont: default-font, background: window-empty, titleFontColor: accent }
|
dialog: {stageBackground: dialogDim, titleFont: default-font, background: window-empty, titleFontColor: accent }
|
||||||
},
|
},
|
||||||
io.anuke.ucore.scene.ui.KeybindDialog$KeybindDialogStyle: {
|
io.anuke.ucore.scene.ui.KeybindDialog$KeybindDialogStyle: {
|
||||||
default: {keyColor: accent, keyNameColor: white, paneStyle: clear},
|
default: {keyColor: accent, keyNameColor: white, controllerColor: menuitem, paneStyle: clear},
|
||||||
},
|
},
|
||||||
io.anuke.ucore.scene.ui.Slider$SliderStyle: {
|
io.anuke.ucore.scene.ui.Slider$SliderStyle: {
|
||||||
default-horizontal: {background: slider, knob: slider-knob, knobOver: slider-knob-over, knobDown: slider-knob-down},
|
default-horizontal: {background: slider, knob: slider-knob, knobOver: slider-knob-over, knobDown: slider-knob-down},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#Autogenerated file. Do not modify.
|
#Autogenerated file. Do not modify.
|
||||||
#Thu Feb 22 23:52:21 EST 2018
|
#Tue Feb 27 19:35:27 EST 2018
|
||||||
version=release
|
version=release
|
||||||
androidBuildCode=282
|
androidBuildCode=313
|
||||||
name=Mindustry
|
name=Mindustry
|
||||||
code=3.3
|
code=3.4
|
||||||
build=custom build
|
build=29
|
||||||
|
|||||||
@@ -15,4 +15,5 @@
|
|||||||
<extend-configuration-property name="gdx.reflect.include" value="io.anuke.mindustry.net.Packet" />
|
<extend-configuration-property name="gdx.reflect.include" value="io.anuke.mindustry.net.Packet" />
|
||||||
<extend-configuration-property name="gdx.reflect.include" value="io.anuke.mindustry.net.Streamable" />
|
<extend-configuration-property name="gdx.reflect.include" value="io.anuke.mindustry.net.Streamable" />
|
||||||
<extend-configuration-property name="gdx.reflect.include" value="io.anuke.mindustry.world.BlockBar" />
|
<extend-configuration-property name="gdx.reflect.include" value="io.anuke.mindustry.world.BlockBar" />
|
||||||
|
<extend-configuration-property name="gdx.reflect.include" value="com.badlogic.gdx.utils.Predicate" />
|
||||||
</module>
|
</module>
|
||||||
@@ -93,7 +93,7 @@ public class Vars{
|
|||||||
public static final int tilesize = 8;
|
public static final int tilesize = 8;
|
||||||
|
|
||||||
public static final Locale[] locales = {new Locale("en"), new Locale("fr", "FR"), new Locale("ru"), new Locale("pl", "PL"),
|
public static final Locale[] locales = {new Locale("en"), new Locale("fr", "FR"), new Locale("ru"), new Locale("pl", "PL"),
|
||||||
new Locale("es", "LA"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID")};
|
new Locale("de"), new Locale("es", "LA"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID")};
|
||||||
|
|
||||||
public static final Color[] playerColors = {
|
public static final Color[] playerColors = {
|
||||||
Color.valueOf("82759a"),
|
Color.valueOf("82759a"),
|
||||||
|
|||||||
@@ -169,8 +169,8 @@ public class Pathfind{
|
|||||||
|
|
||||||
/**Reset and clear the paths.*/
|
/**Reset and clear the paths.*/
|
||||||
public void resetPaths(){
|
public void resetPaths(){
|
||||||
for(SpawnPoint point : world.getSpawns()){
|
for(int i = 0; i < world.getSpawns().size; i ++){
|
||||||
resetPathFor(point);
|
resetPathFor(world.getSpawns().get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ public class Logic extends Module {
|
|||||||
|
|
||||||
if(world.getCore() != null && world.getCore().block() != ProductionBlocks.core && !state.gameOver){
|
if(world.getCore() != null && world.getCore().block() != ProductionBlocks.core && !state.gameOver){
|
||||||
state.gameOver = true;
|
state.gameOver = true;
|
||||||
NetEvents.handleGameOver();
|
if(Net.server()) NetEvents.handleGameOver();
|
||||||
Events.fire(GameOverEvent.class);
|
Events.fire(GameOverEvent.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import io.anuke.mindustry.net.Net.SendMode;
|
|||||||
import io.anuke.mindustry.net.NetworkIO;
|
import io.anuke.mindustry.net.NetworkIO;
|
||||||
import io.anuke.mindustry.net.Packets.*;
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
import io.anuke.mindustry.resource.Item;
|
import io.anuke.mindustry.resource.Item;
|
||||||
|
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.mindustry.world.blocks.ProductionBlocks;
|
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||||
import io.anuke.ucore.core.Timers;
|
import io.anuke.ucore.core.Timers;
|
||||||
@@ -49,6 +51,7 @@ public class NetClient extends Module {
|
|||||||
public NetClient(){
|
public NetClient(){
|
||||||
|
|
||||||
Net.handleClient(Connect.class, packet -> {
|
Net.handleClient(Connect.class, packet -> {
|
||||||
|
player.isAdmin = false;
|
||||||
|
|
||||||
Net.setClientLoaded(false);
|
Net.setClientLoaded(false);
|
||||||
recieved.clear();
|
recieved.clear();
|
||||||
@@ -159,12 +162,22 @@ public class NetClient extends Module {
|
|||||||
state.wavetime = packet.countdown;
|
state.wavetime = packet.countdown;
|
||||||
state.wave = packet.wave;
|
state.wave = packet.wave;
|
||||||
|
|
||||||
//removed: messing with time isn't necessary anymore
|
|
||||||
//Timers.resetTime(packet.time + (float) (TimeUtils.timeSinceMillis(packet.timestamp) / 1000.0 * 60.0));
|
|
||||||
|
|
||||||
ui.hudfrag.updateItems();
|
ui.hudfrag.updateItems();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Net.handleClient(PlacePacket.class, (packet) -> {
|
||||||
|
Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false);
|
||||||
|
|
||||||
|
if(packet.playerid == player.id){
|
||||||
|
Tile tile = world.tile(packet.x, packet.y);
|
||||||
|
if(tile != null) Block.getByID(packet.block).placed(tile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Net.handleClient(BreakPacket.class, (packet) -> {
|
||||||
|
Placement.breakBlock(packet.x, packet.y, true, false);
|
||||||
|
});
|
||||||
|
|
||||||
Net.handleClient(EntitySpawnPacket.class, packet -> {
|
Net.handleClient(EntitySpawnPacket.class, packet -> {
|
||||||
EntityGroup group = packet.group;
|
EntityGroup group = packet.group;
|
||||||
|
|
||||||
@@ -316,6 +329,22 @@ public class NetClient extends Module {
|
|||||||
r.run();
|
r.run();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Net.handleClient(NetErrorPacket.class, packet -> {
|
||||||
|
ui.showError(packet.message);
|
||||||
|
disconnectQuietly();
|
||||||
|
});
|
||||||
|
|
||||||
|
Net.handleClient(PlayerAdminPacket.class, packet -> {
|
||||||
|
Player player = playerGroup.getByID(packet.id);
|
||||||
|
player.isAdmin = packet.admin;
|
||||||
|
ui.listfrag.rebuild();
|
||||||
|
});
|
||||||
|
|
||||||
|
Net.handleClient(TracePacket.class, packet -> {
|
||||||
|
Player player = playerGroup.getByID(packet.info.playerid);
|
||||||
|
ui.traces.show(player, packet.info);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -329,12 +358,6 @@ public class NetClient extends Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO remove.
|
|
||||||
public void test(){
|
|
||||||
gotData = false;
|
|
||||||
connecting = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasData(){
|
public boolean hasData(){
|
||||||
return gotData;
|
return gotData;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ import io.anuke.mindustry.entities.Player;
|
|||||||
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.Packets.*;
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
import io.anuke.mindustry.resource.Recipe;
|
|
||||||
import io.anuke.mindustry.resource.Recipes;
|
|
||||||
import io.anuke.mindustry.resource.Upgrade;
|
import io.anuke.mindustry.resource.Upgrade;
|
||||||
import io.anuke.mindustry.resource.Weapon;
|
import io.anuke.mindustry.resource.Weapon;
|
||||||
import io.anuke.mindustry.world.Block;
|
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.ucore.modules.Module;
|
import io.anuke.ucore.modules.Module;
|
||||||
|
|
||||||
@@ -25,23 +22,6 @@ public class NetCommon extends Module {
|
|||||||
weapon.shoot(player, packet.x, packet.y, packet.rotation);
|
weapon.shoot(player, packet.x, packet.y, packet.rotation);
|
||||||
});
|
});
|
||||||
|
|
||||||
Net.handle(PlacePacket.class, (packet) -> {
|
|
||||||
if(headless)
|
|
||||||
world.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation);
|
|
||||||
else
|
|
||||||
control.input().placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false);
|
|
||||||
|
|
||||||
Recipe recipe = Recipes.getByResult(Block.getByID(packet.block));
|
|
||||||
if (recipe != null) state.inventory.removeItems(recipe.requirements);
|
|
||||||
});
|
|
||||||
|
|
||||||
Net.handle(BreakPacket.class, (packet) -> {
|
|
||||||
if(headless)
|
|
||||||
world.removeBlock(world.tile(packet.x, packet.y));
|
|
||||||
else
|
|
||||||
control.input().breakBlockInternal(packet.x, packet.y, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
Net.handle(ChatPacket.class, (packet) -> {
|
Net.handle(ChatPacket.class, (packet) -> {
|
||||||
ui.chatfrag.addMessage(packet.text, colorizeName(packet.id, packet.name));
|
ui.chatfrag.addMessage(packet.text, colorizeName(packet.id, packet.name));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,14 +7,15 @@ 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.Platform;
|
||||||
import io.anuke.mindustry.io.Version;
|
import io.anuke.mindustry.io.Version;
|
||||||
|
import io.anuke.mindustry.net.Administration;
|
||||||
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.NetConnection;
|
import io.anuke.mindustry.net.NetConnection;
|
||||||
import io.anuke.mindustry.net.NetworkIO;
|
import io.anuke.mindustry.net.NetworkIO;
|
||||||
import io.anuke.mindustry.net.Packets.*;
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
import io.anuke.mindustry.resource.Upgrade;
|
import io.anuke.mindustry.resource.*;
|
||||||
import io.anuke.mindustry.resource.UpgradeRecipes;
|
import io.anuke.mindustry.world.Block;
|
||||||
import io.anuke.mindustry.resource.Weapon;
|
import io.anuke.mindustry.world.Placement;
|
||||||
import io.anuke.mindustry.world.Tile;
|
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;
|
||||||
@@ -41,6 +42,8 @@ public class NetServer extends Module{
|
|||||||
private final static int timerStateSync = 1;
|
private final static int timerStateSync = 1;
|
||||||
private final static int timerBlockSync = 2;
|
private final static int timerBlockSync = 2;
|
||||||
|
|
||||||
|
public final Administration admins = new Administration();
|
||||||
|
|
||||||
/**Maps connection IDs to players.*/
|
/**Maps connection IDs to players.*/
|
||||||
private IntMap<Player> connections = new IntMap<>();
|
private IntMap<Player> connections = new IntMap<>();
|
||||||
private ObjectMap<String, ByteArray> weapons = new ObjectMap<>();
|
private ObjectMap<String, ByteArray> weapons = new ObjectMap<>();
|
||||||
@@ -51,18 +54,33 @@ public class NetServer extends Module{
|
|||||||
|
|
||||||
Events.on(GameOverEvent.class, () -> weapons.clear());
|
Events.on(GameOverEvent.class, () -> weapons.clear());
|
||||||
|
|
||||||
Net.handleServer(Connect.class, (id, connect) -> {});
|
Net.handleServer(Connect.class, (id, connect) -> {
|
||||||
|
if(admins.isBanned(connect.addressTCP)){
|
||||||
|
Net.kickConnection(id, KickReason.banned);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Net.handleServer(ConnectPacket.class, (id, packet) -> {
|
Net.handleServer(ConnectPacket.class, (id, packet) -> {
|
||||||
|
if(Net.getConnection(id) == null ||
|
||||||
|
admins.isBanned(Net.getConnection(id).address)) return;
|
||||||
|
|
||||||
if(packet.version != Version.build && packet.version != -1 && Version.build != -1){ //ignore 'custom builds' on both ends
|
String ip = Net.getConnection(id).address;
|
||||||
|
|
||||||
|
admins.setKnownName(ip, packet.name);
|
||||||
|
|
||||||
|
if(packet.version != Version.build && Version.build != -1 && packet.version != -1){
|
||||||
Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
Net.kickConnection(id, packet.version > Version.build ? KickReason.serverOutdated : KickReason.clientOutdated);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(packet.version == -1){
|
||||||
|
admins.getTrace(ip).modclient = true;
|
||||||
|
}
|
||||||
|
|
||||||
Log.info("Sending data to player '{0}' / {1}", packet.name, id);
|
Log.info("Sending data to player '{0}' / {1}", packet.name, id);
|
||||||
|
|
||||||
Player player = new Player();
|
Player player = new Player();
|
||||||
|
player.isAdmin = admins.isAdmin(Net.getConnection(id).address);
|
||||||
player.clientid = id;
|
player.clientid = id;
|
||||||
player.name = packet.name;
|
player.name = packet.name;
|
||||||
player.isAndroid = packet.android;
|
player.isAndroid = packet.android;
|
||||||
@@ -72,6 +90,8 @@ public class NetServer extends Module{
|
|||||||
player.color.set(packet.color);
|
player.color.set(packet.color);
|
||||||
connections.put(id, player);
|
connections.put(id, player);
|
||||||
|
|
||||||
|
admins.getTrace(ip).playerid = player.id;
|
||||||
|
|
||||||
if(world.getMap().custom){
|
if(world.getMap().custom){
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
NetworkIO.writeMap(world.getMap(), stream);
|
NetworkIO.writeMap(world.getMap(), stream);
|
||||||
@@ -92,7 +112,7 @@ public class NetServer extends Module{
|
|||||||
Player player = connections.get(id);
|
Player player = connections.get(id);
|
||||||
|
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
NetworkIO.writeWorld(player.id, weapons.get(player.name, new ByteArray()), stream);
|
NetworkIO.writeWorld(player, weapons.get(player.name, new ByteArray()), stream);
|
||||||
WorldData data = new WorldData();
|
WorldData data = new WorldData();
|
||||||
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
||||||
Net.sendStream(id, data);
|
Net.sendStream(id, data);
|
||||||
@@ -145,19 +165,53 @@ public class NetServer extends Module{
|
|||||||
|
|
||||||
Net.handleServer(PlacePacket.class, (id, packet) -> {
|
Net.handleServer(PlacePacket.class, (id, packet) -> {
|
||||||
packet.playerid = connections.get(id).id;
|
packet.playerid = connections.get(id).id;
|
||||||
Net.sendExcept(id, packet, SendMode.tcp);
|
|
||||||
|
Block block = Block.getByID(packet.block);
|
||||||
|
|
||||||
|
if(!Placement.validPlace(packet.x, packet.y, block)) return;
|
||||||
|
|
||||||
|
Recipe recipe = Recipes.getByResult(block);
|
||||||
|
|
||||||
|
if(recipe == null) return;
|
||||||
|
|
||||||
|
state.inventory.removeItems(recipe.requirements);
|
||||||
|
|
||||||
|
Placement.placeBlock(packet.x, packet.y, block, packet.rotation, true, false);
|
||||||
|
|
||||||
|
admins.getTrace(Net.getConnection(id).address).lastBlockPlaced = block;
|
||||||
|
admins.getTrace(Net.getConnection(id).address).totalBlocksPlaced ++;
|
||||||
|
|
||||||
|
Net.send(packet, SendMode.tcp);
|
||||||
});
|
});
|
||||||
|
|
||||||
Net.handleServer(BreakPacket.class, (id, packet) -> {
|
Net.handleServer(BreakPacket.class, (id, packet) -> {
|
||||||
packet.playerid = connections.get(id).id;
|
packet.playerid = connections.get(id).id;
|
||||||
Net.sendExcept(id, packet, SendMode.tcp);
|
|
||||||
|
if(!Placement.validBreak(packet.x, packet.y)) return;
|
||||||
|
|
||||||
|
Block block = Placement.breakBlock(packet.x, packet.y, true, false);
|
||||||
|
|
||||||
|
if(block != null) {
|
||||||
|
admins.getTrace(Net.getConnection(id).address).lastBlockBroken = block;
|
||||||
|
admins.getTrace(Net.getConnection(id).address).totalBlocksBroken++;
|
||||||
|
if (block.update || block.destructible)
|
||||||
|
admins.getTrace(Net.getConnection(id).address).structureBlocksBroken++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Net.send(packet, SendMode.tcp);
|
||||||
});
|
});
|
||||||
|
|
||||||
Net.handleServer(ChatPacket.class, (id, packet) -> {
|
Net.handleServer(ChatPacket.class, (id, packet) -> {
|
||||||
|
if(!Timers.get("chatFlood" + id, 20)){
|
||||||
|
ChatPacket warn = new ChatPacket();
|
||||||
|
warn.text = "[scarlet]You are sending messages too quickly.";
|
||||||
|
Net.sendTo(id, warn, SendMode.tcp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Player player = connections.get(id);
|
Player player = connections.get(id);
|
||||||
packet.name = player.name;
|
packet.name = player.name;
|
||||||
packet.id = player.id;
|
packet.id = player.id;
|
||||||
Net.sendExcept(player.clientid, packet, SendMode.tcp);
|
Net.send(packet, SendMode.tcp);
|
||||||
});
|
});
|
||||||
|
|
||||||
Net.handleServer(UpgradePacket.class, (id, packet) -> {
|
Net.handleServer(UpgradePacket.class, (id, packet) -> {
|
||||||
@@ -200,6 +254,39 @@ public class NetServer extends Module{
|
|||||||
packet.id = connections.get(id).id;
|
packet.id = connections.get(id).id;
|
||||||
Net.sendExcept(id, packet, SendMode.tcp);
|
Net.sendExcept(id, packet, SendMode.tcp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Net.handleServer(AdministerRequestPacket.class, (id, packet) -> {
|
||||||
|
Player player = connections.get(id);
|
||||||
|
|
||||||
|
if(!player.isAdmin){
|
||||||
|
Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.",
|
||||||
|
player.name, Net.getConnection(player.clientid).address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player other = playerGroup.getByID(packet.id);
|
||||||
|
|
||||||
|
if(other == null || other.isAdmin){
|
||||||
|
Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String ip = Net.getConnection(other.clientid).address;
|
||||||
|
|
||||||
|
if(packet.action == AdminAction.ban){
|
||||||
|
admins.banPlayer(ip);
|
||||||
|
Net.kickConnection(other.clientid, KickReason.banned);
|
||||||
|
Log.info("&lc{0} has banned {1}.", player.name, other.name);
|
||||||
|
}else if(packet.action == AdminAction.kick){
|
||||||
|
Net.kickConnection(other.clientid, KickReason.kick);
|
||||||
|
Log.info("&lc{0} has kicked {1}.", player.name, other.name);
|
||||||
|
}else if(packet.action == AdminAction.trace){
|
||||||
|
TracePacket trace = new TracePacket();
|
||||||
|
trace.info = admins.getTrace(ip);
|
||||||
|
Net.sendTo(id, trace, SendMode.tcp);
|
||||||
|
Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(){
|
public void update(){
|
||||||
@@ -221,6 +308,7 @@ public class NetServer extends Module{
|
|||||||
|
|
||||||
public void reset(){
|
public void reset(){
|
||||||
weapons.clear();
|
weapons.clear();
|
||||||
|
admins.clearTraces();
|
||||||
}
|
}
|
||||||
|
|
||||||
void sync(){
|
void sync(){
|
||||||
|
|||||||
@@ -264,8 +264,14 @@ public class Renderer extends RendererModule{
|
|||||||
Draw.color();
|
Draw.color();
|
||||||
Draw.tcolor(player.getColor());
|
Draw.tcolor(player.getColor());
|
||||||
Draw.text(player.name, player.x, player.y + 8);
|
Draw.text(player.name, player.x, player.y + 8);
|
||||||
Draw.tcolor();
|
|
||||||
}
|
if(player.isAdmin){
|
||||||
|
Draw.color(player.getColor());
|
||||||
|
float s = 3f;
|
||||||
|
Draw.rect("icon-admin-small", player.x + layout.width/2f + 2 + 1, player.y + 7f, s, s);
|
||||||
|
}
|
||||||
|
Draw.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Pools.free(layout);
|
Pools.free(layout);
|
||||||
Draw.tscl(fontscale);
|
Draw.tscl(fontscale);
|
||||||
@@ -274,6 +280,7 @@ public class Renderer extends RendererModule{
|
|||||||
void drawEnemyMarkers(){
|
void drawEnemyMarkers(){
|
||||||
Graphics.surface(indicatorSurface);
|
Graphics.surface(indicatorSurface);
|
||||||
Draw.color(Color.RED);
|
Draw.color(Color.RED);
|
||||||
|
|
||||||
for(Enemy enemy : enemyGroup.all()) {
|
for(Enemy enemy : enemyGroup.all()) {
|
||||||
|
|
||||||
if (rect.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y)
|
if (rect.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y)
|
||||||
@@ -409,8 +416,10 @@ public class Renderer extends RendererModule{
|
|||||||
Lines.dashCircle(spawn.start.worldx(), spawn.start.worldy(), enemyspawnspace);
|
Lines.dashCircle(spawn.start.worldx(), spawn.start.worldy(), enemyspawnspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
Draw.color(Color.LIME);
|
if(world.getCore() != null) {
|
||||||
Lines.poly(world.getSpawnX(), world.getSpawnY(), 4, 6f, Timers.time()*2f);
|
Draw.color(Color.LIME);
|
||||||
|
Lines.poly(world.getSpawnX(), world.getSpawnY(), 4, 6f, Timers.time() * 2f);
|
||||||
|
}
|
||||||
|
|
||||||
if(input.breakMode == PlaceMode.holdDelete)
|
if(input.breakMode == PlaceMode.holdDelete)
|
||||||
input.breakMode.draw(tilex, tiley, 0, 0);
|
input.breakMode.draw(tilex, tiley, 0, 0);
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ public class ThreadHandler {
|
|||||||
public ThreadHandler(ThreadProvider impl){
|
public ThreadHandler(ThreadProvider impl){
|
||||||
this.impl = impl;
|
this.impl = impl;
|
||||||
|
|
||||||
Timers.setDeltaProvider(() -> impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f);
|
Timers.setDeltaProvider(() ->{
|
||||||
|
float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f;
|
||||||
|
return Float.isNaN(result) ? 1f : result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(Runnable r){
|
public void run(Runnable r){
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ public class UI extends SceneModule{
|
|||||||
public ControlsDialog controls;
|
public ControlsDialog controls;
|
||||||
public MapEditorDialog editor;
|
public MapEditorDialog editor;
|
||||||
public LanguageDialog language;
|
public LanguageDialog language;
|
||||||
|
public BansDialog bans;
|
||||||
|
public AdminsDialog admins;
|
||||||
|
public TraceDialog traces;
|
||||||
|
|
||||||
public final MenuFragment menufrag = new MenuFragment();
|
public final MenuFragment menufrag = new MenuFragment();
|
||||||
public final ToolFragment toolfrag = new ToolFragment();
|
public final ToolFragment toolfrag = new ToolFragment();
|
||||||
@@ -150,6 +153,9 @@ public class UI extends SceneModule{
|
|||||||
paused = new PausedDialog();
|
paused = new PausedDialog();
|
||||||
about = new AboutDialog();
|
about = new AboutDialog();
|
||||||
host = new HostDialog();
|
host = new HostDialog();
|
||||||
|
bans = new BansDialog();
|
||||||
|
admins = new AdminsDialog();
|
||||||
|
traces = new TraceDialog();
|
||||||
|
|
||||||
build.begin(scene);
|
build.begin(scene);
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,7 @@ import io.anuke.mindustry.ai.Pathfind;
|
|||||||
import io.anuke.mindustry.entities.TileEntity;
|
import io.anuke.mindustry.entities.TileEntity;
|
||||||
import io.anuke.mindustry.game.SpawnPoint;
|
import io.anuke.mindustry.game.SpawnPoint;
|
||||||
import io.anuke.mindustry.io.Maps;
|
import io.anuke.mindustry.io.Maps;
|
||||||
import io.anuke.mindustry.world.Block;
|
import io.anuke.mindustry.world.*;
|
||||||
import io.anuke.mindustry.world.Map;
|
|
||||||
import io.anuke.mindustry.world.Tile;
|
|
||||||
import io.anuke.mindustry.world.WorldGenerator;
|
|
||||||
import io.anuke.mindustry.world.blocks.Blocks;
|
import io.anuke.mindustry.world.blocks.Blocks;
|
||||||
import io.anuke.mindustry.world.blocks.DistributionBlocks;
|
import io.anuke.mindustry.world.blocks.DistributionBlocks;
|
||||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||||
@@ -22,7 +19,8 @@ import io.anuke.ucore.modules.Module;
|
|||||||
import io.anuke.ucore.util.Mathf;
|
import io.anuke.ucore.util.Mathf;
|
||||||
import io.anuke.ucore.util.Tmp;
|
import io.anuke.ucore.util.Tmp;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.control;
|
||||||
|
import static io.anuke.mindustry.Vars.tilesize;
|
||||||
|
|
||||||
public class World extends Module{
|
public class World extends Module{
|
||||||
private int seed;
|
private int seed;
|
||||||
@@ -182,7 +180,7 @@ public class World extends Module{
|
|||||||
|
|
||||||
core = WorldGenerator.generate(map.pixmap, tiles, spawns);
|
core = WorldGenerator.generate(map.pixmap, tiles, spawns);
|
||||||
|
|
||||||
placeBlock(core.x, core.y, ProductionBlocks.core, 0);
|
Placement.placeBlock(core.x, core.y, ProductionBlocks.core, 0, false, false);
|
||||||
|
|
||||||
if(!map.name.equals("tutorial")){
|
if(!map.name.equals("tutorial")){
|
||||||
setDefaultBlocks();
|
setDefaultBlocks();
|
||||||
@@ -233,7 +231,7 @@ public class World extends Module{
|
|||||||
public int getSeed(){
|
public int getSeed(){
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeBlock(Tile tile){
|
public void removeBlock(Tile tile){
|
||||||
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
||||||
tile.setBlock(Blocks.air);
|
tile.setBlock(Blocks.air);
|
||||||
@@ -246,32 +244,6 @@ public class World extends Module{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void placeBlock(int x, int y, Block result, int rotation){
|
|
||||||
Tile tile = world.tile(x, y);
|
|
||||||
|
|
||||||
//just in case
|
|
||||||
if(tile == null) return;
|
|
||||||
|
|
||||||
tile.setBlock(result, rotation);
|
|
||||||
|
|
||||||
if(result.isMultiblock()){
|
|
||||||
int offsetx = -(result.width-1)/2;
|
|
||||||
int offsety = -(result.height-1)/2;
|
|
||||||
|
|
||||||
for(int dx = 0; dx < result.width; dx ++){
|
|
||||||
for(int dy = 0; dy < result.height; dy ++){
|
|
||||||
int worldx = dx + offsetx + x;
|
|
||||||
int worldy = dy + offsety + y;
|
|
||||||
if(!(worldx == x && worldy == y)){
|
|
||||||
Tile toplace = world.tile(worldx, worldy);
|
|
||||||
if(toplace != null)
|
|
||||||
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TileEntity findTileTarget(float x, float y, Tile tile, float range, boolean damaged){
|
public TileEntity findTileTarget(float x, float y, Tile tile, float range, boolean damaged){
|
||||||
Entity closest = null;
|
Entity closest = null;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class Player extends SyncEntity{
|
|||||||
|
|
||||||
public String name = "name";
|
public String name = "name";
|
||||||
public boolean isAndroid;
|
public boolean isAndroid;
|
||||||
|
public boolean isAdmin;
|
||||||
public Color color = new Color();
|
public Color color = new Color();
|
||||||
|
|
||||||
public Weapon weaponLeft = Weapon.blaster;
|
public Weapon weaponLeft = Weapon.blaster;
|
||||||
@@ -44,7 +45,7 @@ public class Player extends SyncEntity{
|
|||||||
public float stucktime = 0f;
|
public float stucktime = 0f;
|
||||||
public boolean dashing = false;
|
public boolean dashing = false;
|
||||||
|
|
||||||
public int clientid;
|
public int clientid = -1;
|
||||||
public boolean isLocal = false;
|
public boolean isLocal = false;
|
||||||
public Timer timer = new Timer(4);
|
public Timer timer = new Timer(4);
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ public class Player extends SyncEntity{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeath(){
|
public void onDeath(){
|
||||||
remove();
|
dead = true;
|
||||||
if(Net.active()){
|
if(Net.active()){
|
||||||
NetEvents.handlePlayerDeath();
|
NetEvents.handlePlayerDeath();
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,7 @@ public class Player extends SyncEntity{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawSmooth(){
|
public void drawSmooth(){
|
||||||
if((debug && (!showPlayer || !showUI)) || (isAndroid && isLocal) || (dead && !isLocal)) return;
|
if((debug && (!showPlayer || !showUI)) || (isAndroid && isLocal) || dead) return;
|
||||||
boolean snap = snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate") && isLocal;
|
boolean snap = snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate") && isLocal;
|
||||||
|
|
||||||
String part = isAndroid ? "ship" : "mech";
|
String part = isAndroid ? "ship" : "mech";
|
||||||
@@ -155,6 +156,8 @@ public class Player extends SyncEntity{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isDead()) return;
|
||||||
|
|
||||||
Tile tile = world.tileWorld(x, y);
|
Tile tile = world.tileWorld(x, y);
|
||||||
|
|
||||||
//if player is in solid block
|
//if player is in solid block
|
||||||
@@ -240,6 +243,7 @@ public class Player extends SyncEntity{
|
|||||||
buffer.put(weaponLeft.id);
|
buffer.put(weaponLeft.id);
|
||||||
buffer.put(weaponRight.id);
|
buffer.put(weaponRight.id);
|
||||||
buffer.put(isAndroid ? 1 : (byte)0);
|
buffer.put(isAndroid ? 1 : (byte)0);
|
||||||
|
buffer.put(isAdmin ? 1 : (byte)0);
|
||||||
buffer.putInt(Color.rgba8888(color));
|
buffer.putInt(Color.rgba8888(color));
|
||||||
buffer.putFloat(x);
|
buffer.putFloat(x);
|
||||||
buffer.putFloat(y);
|
buffer.putFloat(y);
|
||||||
@@ -254,6 +258,7 @@ public class Player extends SyncEntity{
|
|||||||
weaponLeft = (Weapon) Upgrade.getByID(buffer.get());
|
weaponLeft = (Weapon) Upgrade.getByID(buffer.get());
|
||||||
weaponRight = (Weapon) Upgrade.getByID(buffer.get());
|
weaponRight = (Weapon) Upgrade.getByID(buffer.get());
|
||||||
isAndroid = buffer.get() == 1;
|
isAndroid = buffer.get() == 1;
|
||||||
|
isAdmin = buffer.get() == 1;
|
||||||
color.set(buffer.getInt());
|
color.set(buffer.getInt());
|
||||||
x = buffer.getFloat();
|
x = buffer.getFloat();
|
||||||
y = buffer.getFloat();
|
y = buffer.getFloat();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import io.anuke.ucore.core.Graphics;
|
|||||||
import io.anuke.ucore.core.Timers;
|
import io.anuke.ucore.core.Timers;
|
||||||
import io.anuke.ucore.entities.Entities;
|
import io.anuke.ucore.entities.Entities;
|
||||||
import io.anuke.ucore.graphics.Draw;
|
import io.anuke.ucore.graphics.Draw;
|
||||||
|
import io.anuke.ucore.graphics.Lines;
|
||||||
import io.anuke.ucore.util.Mathf;
|
import io.anuke.ucore.util.Mathf;
|
||||||
import io.anuke.ucore.util.Strings;
|
import io.anuke.ucore.util.Strings;
|
||||||
|
|
||||||
@@ -82,6 +83,13 @@ public class EnemyType {
|
|||||||
|
|
||||||
Graphics.flush();
|
Graphics.flush();
|
||||||
|
|
||||||
|
if(isCalculating(enemy)){
|
||||||
|
Draw.color(Color.SKY);
|
||||||
|
Lines.polySeg(20, 0, 4, enemy.x, enemy.y, 11f, Timers.time() * 2f + enemy.id*52f);
|
||||||
|
Lines.polySeg(20, 0, 4, enemy.x, enemy.y, 11f, Timers.time() * 2f + enemy.id*52f + 180f);
|
||||||
|
Draw.color();
|
||||||
|
}
|
||||||
|
|
||||||
if(showPaths){
|
if(showPaths){
|
||||||
Draw.tscl(0.25f);
|
Draw.tscl(0.25f);
|
||||||
Draw.text((int)enemy.idletime + " " + enemy.node + " " + enemy.id + "\n" + Strings.toFixed(enemy.totalMove.x, 2) + ", "
|
Draw.text((int)enemy.idletime + " " + enemy.node + " " + enemy.id + "\n" + Strings.toFixed(enemy.totalMove.x, 2) + ", "
|
||||||
@@ -100,9 +108,10 @@ public class EnemyType {
|
|||||||
enemy.hitTime -= Timers.delta();
|
enemy.hitTime -= Timers.delta();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(enemy.lane >= world.getSpawns().size) enemy.lane = 0;
|
if(enemy.lane >= world.getSpawns().size || enemy.lane < 0) enemy.lane = 0;
|
||||||
|
|
||||||
boolean waiting = world.getSpawns().get(enemy.lane).pathTiles == null || enemy.node <= 0;
|
boolean waiting = enemy.lane >= world.getSpawns().size || enemy.lane < 0
|
||||||
|
|| world.getSpawns().get(enemy.lane).pathTiles == null || enemy.node <= 0;
|
||||||
|
|
||||||
move(enemy);
|
move(enemy);
|
||||||
|
|
||||||
@@ -156,6 +165,8 @@ public class EnemyType {
|
|||||||
|
|
||||||
Tile core = world.getCore();
|
Tile core = world.getCore();
|
||||||
|
|
||||||
|
if(core == null) return;
|
||||||
|
|
||||||
if(enemy.idletime > maxIdleLife && enemy.node > 0){
|
if(enemy.idletime > maxIdleLife && enemy.node > 0){
|
||||||
enemy.onDeath();
|
enemy.onDeath();
|
||||||
return;
|
return;
|
||||||
@@ -189,7 +200,7 @@ public class EnemyType {
|
|||||||
}else if(dst < avoidRange){
|
}else if(dst < avoidRange){
|
||||||
calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed);
|
calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed);
|
||||||
shift.add(calc.scl(1.1f));
|
shift.add(calc.scl(1.1f));
|
||||||
}else if(dst < attractRange && !nearCore){
|
}else if(dst < attractRange && !nearCore && !isCalculating(enemy)){
|
||||||
calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed);
|
calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed);
|
||||||
shift.add(calc.scl(-1));
|
shift.add(calc.scl(-1));
|
||||||
}
|
}
|
||||||
@@ -217,7 +228,8 @@ public class EnemyType {
|
|||||||
|
|
||||||
//no tile found
|
//no tile found
|
||||||
if(enemy.target == null){
|
if(enemy.target == null){
|
||||||
enemy.target = Entities.getClosest(playerGroup, enemy.x, enemy.y, range, e -> !((Player)e).isAndroid);
|
enemy.target = Entities.getClosest(playerGroup, enemy.x, enemy.y, range, e -> !((Player)e).isAndroid &&
|
||||||
|
!((Player)e).isDead());
|
||||||
}
|
}
|
||||||
}else if(nearCore){
|
}else if(nearCore){
|
||||||
enemy.target = world.getCore().entity;
|
enemy.target = world.getCore().entity;
|
||||||
@@ -267,6 +279,10 @@ public class EnemyType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isCalculating(Enemy enemy){
|
||||||
|
return enemy.node < 0 && !Net.client();
|
||||||
|
}
|
||||||
|
|
||||||
public static EnemyType getByID(byte id){
|
public static EnemyType getByID(byte id){
|
||||||
return types.get(id);
|
return types.get(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,4 +56,9 @@ public class TargetType extends EnemyType {
|
|||||||
new Enemy(EnemyTypes.target).set(enemy.x, enemy.y).add();
|
new Enemy(EnemyTypes.target).set(enemy.x, enemy.y).add();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCalculating(Enemy enemy){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public class BlockRenderer{
|
|||||||
|
|
||||||
OrthographicCamera camera = Core.camera;
|
OrthographicCamera camera = Core.camera;
|
||||||
|
|
||||||
Graphics.end();
|
if(Graphics.drawing()) Graphics.end();
|
||||||
|
|
||||||
int crangex = (int)(camera.viewportWidth * camera.zoom / (chunksize * tilesize))+1;
|
int crangex = (int)(camera.viewportWidth * camera.zoom / (chunksize * tilesize))+1;
|
||||||
int crangey = (int)(camera.viewportHeight * camera.zoom / (chunksize * tilesize))+1;
|
int crangey = (int)(camera.viewportHeight * camera.zoom / (chunksize * tilesize))+1;
|
||||||
|
|||||||
@@ -2,26 +2,16 @@ package io.anuke.mindustry.input;
|
|||||||
|
|
||||||
import com.badlogic.gdx.InputAdapter;
|
import com.badlogic.gdx.InputAdapter;
|
||||||
import com.badlogic.gdx.math.GridPoint2;
|
import com.badlogic.gdx.math.GridPoint2;
|
||||||
import com.badlogic.gdx.math.Rectangle;
|
|
||||||
import com.badlogic.gdx.math.Vector2;
|
import com.badlogic.gdx.math.Vector2;
|
||||||
import com.badlogic.gdx.utils.Array;
|
|
||||||
import io.anuke.mindustry.entities.Player;
|
|
||||||
import io.anuke.mindustry.game.SpawnPoint;
|
|
||||||
import io.anuke.mindustry.graphics.Fx;
|
|
||||||
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.mindustry.resource.ItemStack;
|
import io.anuke.mindustry.resource.ItemStack;
|
||||||
import io.anuke.mindustry.resource.Recipe;
|
import io.anuke.mindustry.resource.Recipe;
|
||||||
import io.anuke.mindustry.resource.Recipes;
|
|
||||||
import io.anuke.mindustry.world.Block;
|
import io.anuke.mindustry.world.Block;
|
||||||
|
import io.anuke.mindustry.world.Placement;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.mindustry.world.blocks.Blocks;
|
import io.anuke.mindustry.world.blocks.Blocks;
|
||||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
|
||||||
import io.anuke.ucore.core.Effects;
|
|
||||||
import io.anuke.ucore.core.Graphics;
|
import io.anuke.ucore.core.Graphics;
|
||||||
import io.anuke.ucore.core.Sounds;
|
|
||||||
import io.anuke.ucore.entities.Entities;
|
|
||||||
import io.anuke.ucore.entities.SolidEntity;
|
|
||||||
import io.anuke.ucore.util.Mathf;
|
import io.anuke.ucore.util.Mathf;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
@@ -35,8 +25,6 @@ public abstract class InputHandler extends InputAdapter{
|
|||||||
public PlaceMode lastPlaceMode = placeMode;
|
public PlaceMode lastPlaceMode = placeMode;
|
||||||
public PlaceMode lastBreakMode = breakMode;
|
public PlaceMode lastBreakMode = breakMode;
|
||||||
|
|
||||||
private Rectangle rect = new Rectangle();
|
|
||||||
|
|
||||||
public abstract void update();
|
public abstract void update();
|
||||||
public abstract float getCursorX();
|
public abstract float getCursorX();
|
||||||
public abstract float getCursorY();
|
public abstract float getCursorY();
|
||||||
@@ -88,39 +76,6 @@ public abstract class InputHandler extends InputAdapter{
|
|||||||
|
|
||||||
public boolean validPlace(int x, int y, Block type){
|
public boolean validPlace(int x, int y, Block type){
|
||||||
|
|
||||||
for(SpawnPoint spawn : world.getSpawns()){
|
|
||||||
if(Vector2.dst(x * tilesize, y * tilesize, spawn.start.worldx(), spawn.start.worldy()) < enemyspawnspace){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rect.setSize(type.width * tilesize, type.height * tilesize);
|
|
||||||
Vector2 offset = type.getPlaceOffset();
|
|
||||||
rect.setCenter(offset.x + x * tilesize, offset.y + y * tilesize);
|
|
||||||
|
|
||||||
synchronized (Entities.entityLock) {
|
|
||||||
for (SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)) {
|
|
||||||
if (e == null) continue; //not sure why this happens?
|
|
||||||
Rectangle rect = e.hitbox.getRect(e.x, e.y);
|
|
||||||
|
|
||||||
if (this.rect.overlaps(rect)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(type.solid || type.solidifes) {
|
|
||||||
for (Player player : playerGroup.all()) {
|
|
||||||
if (!player.isAndroid && rect.overlaps(player.hitbox.getRect(player.x, player.y))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tile tile = world.tile(x, y);
|
|
||||||
|
|
||||||
if(tile == null || (isSpawnPoint(tile) && (type.solidifes || type.solid))) return false;
|
|
||||||
|
|
||||||
if(!type.isMultiblock() && control.tutorial().active() &&
|
if(!type.isMultiblock() && control.tutorial().active() &&
|
||||||
control.tutorial().showBlock()){
|
control.tutorial().showBlock()){
|
||||||
|
|
||||||
@@ -135,40 +90,11 @@ public abstract class InputHandler extends InputAdapter{
|
|||||||
}else if(control.tutorial().active()){
|
}else if(control.tutorial().active()){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(type.isMultiblock()){
|
|
||||||
int offsetx = -(type.width-1)/2;
|
|
||||||
int offsety = -(type.height-1)/2;
|
|
||||||
for(int dx = 0; dx < type.width; dx ++){
|
|
||||||
for(int dy = 0; dy < type.height; dy ++){
|
|
||||||
Tile other = world.tile(x + dx + offsetx, y + dy + offsety);
|
|
||||||
if(other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) || isSpawnPoint(other)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}else{
|
|
||||||
if(tile.block() != type && (type.canReplace(tile.block()) || tile.block().alwaysReplace) && tile.block().isMultiblock() == type.isMultiblock()){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return tile.block() == Blocks.air;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSpawnPoint(Tile tile){
|
return Placement.validPlace(x, y, type);
|
||||||
return tile != null && tile.x == world.getCore().x && tile.y == world.getCore().y - 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validBreak(int x, int y){
|
public boolean validBreak(int x, int y){
|
||||||
Tile tile = world.tile(x, y);
|
|
||||||
|
|
||||||
if(tile == null || tile.block() == ProductionBlocks.core) return false;
|
|
||||||
|
|
||||||
if(tile.isLinked() && tile.getLinked().block() == ProductionBlocks.core){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(control.tutorial().active()){
|
if(control.tutorial().active()){
|
||||||
|
|
||||||
if(control.tutorial().showBlock()){
|
if(control.tutorial().showBlock()){
|
||||||
@@ -185,102 +111,26 @@ public abstract class InputHandler extends InputAdapter{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tile.breakable();
|
return Placement.validBreak(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
public void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
||||||
|
if(!Net.client()){
|
||||||
|
Placement.placeBlock(x, y, result, rotation, effects, sound);
|
||||||
|
Tile tile = world.tile(x, y);
|
||||||
|
if(tile != null) result.placed(tile);
|
||||||
|
}
|
||||||
|
|
||||||
placeBlockInternal(x, y, result, rotation, effects, sound);
|
if(Net.active()){
|
||||||
|
|
||||||
Tile tile = world.tile(x, y);
|
|
||||||
|
|
||||||
if(tile != null) result.placed(tile);
|
|
||||||
|
|
||||||
if(Net.active() && result != ProductionBlocks.core){
|
|
||||||
NetEvents.handlePlace(x, y, result, rotation);
|
NetEvents.handlePlace(x, y, result, rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void placeBlockInternal(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
|
||||||
Tile tile = world.tile(x, y);
|
|
||||||
|
|
||||||
//just in case
|
|
||||||
if(tile == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tile.setBlock(result, rotation);
|
|
||||||
|
|
||||||
if(result.isMultiblock()){
|
|
||||||
int offsetx = -(result.width-1)/2;
|
|
||||||
int offsety = -(result.height-1)/2;
|
|
||||||
|
|
||||||
for(int dx = 0; dx < result.width; dx ++){
|
|
||||||
for(int dy = 0; dy < result.height; dy ++){
|
|
||||||
int worldx = dx + offsetx + x;
|
|
||||||
int worldy = dy + offsety + y;
|
|
||||||
if(!(worldx == x && worldy == y)){
|
|
||||||
Tile toplace = world.tile(worldx, worldy);
|
|
||||||
if(toplace != null)
|
|
||||||
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(effects) Effects.effect(Fx.place, worldx * tilesize, worldy * tilesize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(effects && sound) Sounds.play("place");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void breakBlock(int x, int y, boolean sound){
|
public void breakBlock(int x, int y, boolean sound){
|
||||||
breakBlockInternal(x, y, sound);
|
if(!Net.client()) Placement.breakBlock(x, y, true, sound);
|
||||||
|
|
||||||
if(Net.active()){
|
if(Net.active()){
|
||||||
NetEvents.handleBreak(x, y);
|
NetEvents.handleBreak(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void breakBlockInternal(int x, int y, boolean sound){
|
|
||||||
Tile tile = world.tile(x, y);
|
|
||||||
|
|
||||||
if(tile == null) return;
|
|
||||||
|
|
||||||
Block block = tile.isLinked() ? tile.getLinked().block() : tile.block();
|
|
||||||
Recipe result = null;
|
|
||||||
|
|
||||||
for(Recipe recipe : Recipes.all()){
|
|
||||||
if(recipe.result == block){
|
|
||||||
result = recipe;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(result != null){
|
|
||||||
for(ItemStack stack : result.requirements){
|
|
||||||
state.inventory.addItem(stack.item, (int)(stack.amount * breakDropAmount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(tile.block().drops != null){
|
|
||||||
state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Effects.shake(3f, 1f, player);
|
|
||||||
if(sound) Sounds.play("break");
|
|
||||||
|
|
||||||
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
|
||||||
tile.setBlock(Blocks.air);
|
|
||||||
Effects.effect(Fx.breakBlock, tile.worldx(), tile.worldy());
|
|
||||||
}else{
|
|
||||||
Tile target = tile.isLinked() ? tile.getLinked() : tile;
|
|
||||||
Array<Tile> removals = target.getLinkedTiles();
|
|
||||||
for(Tile toremove : removals){
|
|
||||||
//note that setting a new block automatically unlinks it
|
|
||||||
toremove.setBlock(Blocks.air);
|
|
||||||
Effects.effect(Fx.breakBlock, toremove.worldx(), toremove.worldy());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,13 +75,14 @@ public class Maps implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loadMaps(){
|
public void loadMaps(){
|
||||||
if(!loadMapFile(Gdx.files.internal("maps/maps.json"))){
|
if(!loadMapFile(Gdx.files.internal("maps/maps.json"), true)){
|
||||||
throw new RuntimeException("Failed to load maps!");
|
throw new RuntimeException("Failed to load maps!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!gwt) {
|
if(!gwt) {
|
||||||
if (!loadMapFile(customMapDirectory.child("maps.json"))) {
|
if (!loadMapFile(customMapDirectory.child("maps.json"), false)) {
|
||||||
try {
|
try {
|
||||||
|
Log.info("Failed to find custom map directory.");
|
||||||
customMapDirectory.child("maps.json").writeString("{}", false);
|
customMapDirectory.child("maps.json").writeString("{}", false);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Failed to create custom map directory!");
|
throw new RuntimeException("Failed to create custom map directory!");
|
||||||
@@ -159,25 +160,30 @@ public class Maps implements Disposable{
|
|||||||
saveMaps(out, customMapDirectory.child("maps.json"));
|
saveMaps(out, customMapDirectory.child("maps.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadMapFile(FileHandle file){
|
private boolean loadMapFile(FileHandle file, boolean logException){
|
||||||
try{
|
try {
|
||||||
Array<Map> arr = json.fromJson(ArrayContainer.class, file).maps;
|
Array<Map> arr = json.fromJson(ArrayContainer.class, file).maps;
|
||||||
if(arr != null){ //can be an empty map file
|
if (arr != null) { //can be an empty map file
|
||||||
for(Map map : arr){
|
for (Map map : arr) {
|
||||||
map.pixmap = new Pixmap(file.sibling(map.name + ".png"));
|
map.pixmap = new Pixmap(file.sibling(map.name + ".png"));
|
||||||
if(!headless) map.texture = new Texture(map.pixmap);
|
if (!headless) map.texture = new Texture(map.pixmap);
|
||||||
maps.put(map.id, map);
|
maps.put(map.id, map);
|
||||||
mapNames.put(map.name, map);
|
mapNames.put(map.name, map);
|
||||||
lastID = Math.max(lastID, map.id);
|
lastID = Math.max(lastID, map.id);
|
||||||
if(!map.custom){
|
if (!map.custom) {
|
||||||
defaultMaps.add(map);
|
defaultMaps.add(map);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}catch(Exception e){
|
}catch (GdxRuntimeException e){
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
Log.err("Failed loading map file: {0}", file);
|
return true;
|
||||||
|
}catch(Exception e){
|
||||||
|
if(logException) {
|
||||||
|
Log.err(e);
|
||||||
|
Log.err("Failed loading map file: {0}", file);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ public class Save15 extends SaveFileVersion {
|
|||||||
for(int y = 0; y < world.height(); y ++){
|
for(int y = 0; y < world.height(); y ++){
|
||||||
Tile tile = world.tile(x, y);
|
Tile tile = world.tile(x, y);
|
||||||
|
|
||||||
if(tile.breakable()){
|
if(tile != null && tile.breakable()){
|
||||||
if(tile.block() instanceof Rock){
|
if(tile.block() instanceof Rock){
|
||||||
totalrocks ++;
|
totalrocks ++;
|
||||||
}else{
|
}else{
|
||||||
@@ -325,7 +325,7 @@ public class Save15 extends SaveFileVersion {
|
|||||||
for (int y = 0; y < world.height(); y++) {
|
for (int y = 0; y < world.height(); y++) {
|
||||||
Tile tile = world.tile(x, y);
|
Tile tile = world.tile(x, y);
|
||||||
|
|
||||||
if (tile.block() instanceof Rock) {
|
if (tile != null && tile.block() instanceof Rock) {
|
||||||
stream.writeInt(tile.packedPosition());
|
stream.writeInt(tile.packedPosition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +338,7 @@ public class Save15 extends SaveFileVersion {
|
|||||||
for(int y = 0; y < world.height(); y ++){
|
for(int y = 0; y < world.height(); y ++){
|
||||||
Tile tile = world.tile(x, y);
|
Tile tile = world.tile(x, y);
|
||||||
|
|
||||||
if(tile.breakable() && !(tile.block() instanceof Rock)){
|
if(tile != null && tile.breakable() && !(tile.block() instanceof Rock)){
|
||||||
|
|
||||||
stream.writeInt(x + y*world.width()); //tile pos
|
stream.writeInt(x + y*world.width()); //tile pos
|
||||||
stream.writeInt(tile.block().id); //block ID
|
stream.writeInt(tile.block().id); //block ID
|
||||||
|
|||||||
125
core/src/io/anuke/mindustry/net/Administration.java
Normal file
125
core/src/io/anuke/mindustry/net/Administration.java
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package io.anuke.mindustry.net;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import com.badlogic.gdx.utils.Json;
|
||||||
|
import com.badlogic.gdx.utils.ObjectMap;
|
||||||
|
import io.anuke.ucore.core.Settings;
|
||||||
|
|
||||||
|
public class Administration {
|
||||||
|
private Json json = new Json();
|
||||||
|
private Array<String> bannedIPS = new Array<>();
|
||||||
|
private Array<String> admins = new Array<>();
|
||||||
|
private ObjectMap<String, String> known = new ObjectMap<>();
|
||||||
|
private ObjectMap<String, TraceInfo> traces = new ObjectMap<>();
|
||||||
|
|
||||||
|
public Administration(){
|
||||||
|
Settings.defaultList(
|
||||||
|
"bans", "{}",
|
||||||
|
"admins", "{}",
|
||||||
|
"knownIPs", "{}"
|
||||||
|
);
|
||||||
|
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TraceInfo getTrace(String ip){
|
||||||
|
if(!traces.containsKey(ip)) traces.put(ip, new TraceInfo(ip));
|
||||||
|
|
||||||
|
return traces.get(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearTraces(){
|
||||||
|
traces.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Sets last known name for an IP.*/
|
||||||
|
public void setKnownName(String ip, String name){
|
||||||
|
known.put(ip, name);
|
||||||
|
saveKnown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Returns the last known name for an IP. Returns 'unknown' if this IP has an unknown username.*/
|
||||||
|
public String getLastName(String ip){
|
||||||
|
return known.get(ip, "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Returns list of banned IPs.*/
|
||||||
|
public Array<String> getBanned(){
|
||||||
|
return bannedIPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Bans a player by IP; returns whether this player was already banned.*/
|
||||||
|
public boolean banPlayer(String ip){
|
||||||
|
if(bannedIPS.contains(ip, false))
|
||||||
|
return false;
|
||||||
|
bannedIPS.add(ip);
|
||||||
|
saveBans();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Unbans a player by IP; returns whether this player was banned in the first place..*/
|
||||||
|
public boolean unbanPlayer(String ip){
|
||||||
|
if(!bannedIPS.contains(ip, false))
|
||||||
|
return false;
|
||||||
|
bannedIPS.removeValue(ip, false);
|
||||||
|
saveBans();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Returns list of banned IPs.*/
|
||||||
|
public Array<String> getAdmins(){
|
||||||
|
return admins;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Makes a player an admin. Returns whether this player was already an admin.*/
|
||||||
|
public boolean adminPlayer(String ip){
|
||||||
|
if(admins.contains(ip, false))
|
||||||
|
return false;
|
||||||
|
admins.add(ip);
|
||||||
|
saveAdmins();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/
|
||||||
|
public boolean unAdminPlayer(String ip){
|
||||||
|
if(!admins.contains(ip, false))
|
||||||
|
return false;
|
||||||
|
admins.removeValue(ip, false);
|
||||||
|
saveAdmins();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBanned(String ip){
|
||||||
|
return bannedIPS.contains(ip, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAdmin(String ip){
|
||||||
|
return admins.contains(ip, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveKnown(){
|
||||||
|
Settings.putString("knownIPs", json.toJson(known));
|
||||||
|
Settings.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveBans(){
|
||||||
|
Settings.putString("bans", json.toJson(bannedIPS));
|
||||||
|
Settings.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveAdmins(){
|
||||||
|
Settings.putString("admins", json.toJson(admins));
|
||||||
|
Settings.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load(){
|
||||||
|
bannedIPS = json.fromJson(Array.class, Settings.getString("bans"));
|
||||||
|
admins = json.fromJson(Array.class, Settings.getString("admins"));
|
||||||
|
known = json.fromJson(ObjectMap.class, Settings.getString("knownIPs"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,11 +3,15 @@ package io.anuke.mindustry.net;
|
|||||||
public class Host {
|
public class Host {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final String address;
|
public final String address;
|
||||||
|
public final String mapname;
|
||||||
|
public final int wave;
|
||||||
public final int players;
|
public final int players;
|
||||||
|
|
||||||
public Host(String name, String address, int players){
|
public Host(String name, String address, String mapname, int wave, int players){
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.players = players;
|
this.players = players;
|
||||||
|
this.mapname = mapname;
|
||||||
|
this.wave = wave;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ public class Net{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**Pings a host in an new thread. If an error occured, failed() should be called with the exception. */
|
/**Pings a host in an new thread. If an error occured, failed() should be called with the exception. */
|
||||||
public static void pingHost(String address, int port, Consumer<Host> valid, Consumer<IOException> failed){
|
public static void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed){
|
||||||
clientProvider.pingHost(address, port, valid, failed);
|
clientProvider.pingHost(address, port, valid, failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ public class Net{
|
|||||||
* Callback should be run on libGDX main thread.*/
|
* Callback should be run on libGDX main thread.*/
|
||||||
void discover(Consumer<Array<Host>> callback);
|
void discover(Consumer<Array<Host>> callback);
|
||||||
/**Ping a host. If an error occured, failed() should be called with the exception. */
|
/**Ping a host. If an error occured, failed() should be called with the exception. */
|
||||||
void pingHost(String address, int port, Consumer<Host> valid, Consumer<IOException> failed);
|
void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed);
|
||||||
/**Close all connections.*/
|
/**Close all connections.*/
|
||||||
void dispose();
|
void dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.anuke.mindustry.net;
|
|||||||
|
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.entities.BulletType;
|
import io.anuke.mindustry.entities.BulletType;
|
||||||
|
import io.anuke.mindustry.entities.Player;
|
||||||
import io.anuke.mindustry.entities.TileEntity;
|
import io.anuke.mindustry.entities.TileEntity;
|
||||||
import io.anuke.mindustry.entities.enemies.Enemy;
|
import io.anuke.mindustry.entities.enemies.Enemy;
|
||||||
import io.anuke.mindustry.net.Net.SendMode;
|
import io.anuke.mindustry.net.Net.SendMode;
|
||||||
@@ -12,8 +13,7 @@ import io.anuke.mindustry.world.Block;
|
|||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.ucore.entities.Entity;
|
import io.anuke.ucore.entities.Entity;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.netCommon;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
import static io.anuke.mindustry.Vars.ui;
|
|
||||||
|
|
||||||
public class NetEvents {
|
public class NetEvents {
|
||||||
|
|
||||||
@@ -99,8 +99,6 @@ public class NetEvents {
|
|||||||
packet.name = Vars.player.name;
|
packet.name = Vars.player.name;
|
||||||
packet.id = Vars.player.id;
|
packet.id = Vars.player.id;
|
||||||
Net.send(packet, SendMode.tcp);
|
Net.send(packet, SendMode.tcp);
|
||||||
|
|
||||||
ui.chatfrag.addMessage(packet.text, netCommon.colorizeName(Vars.player.id, Vars.player.name));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void handleShoot(Weapon weapon, float x, float y, float angle){
|
public static void handleShoot(Weapon weapon, float x, float y, float angle){
|
||||||
@@ -152,4 +150,27 @@ public class NetEvents {
|
|||||||
packet.itemid = (byte)item.id;
|
packet.itemid = (byte)item.id;
|
||||||
Net.send(packet, SendMode.udp);
|
Net.send(packet, SendMode.udp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void handleAdminSet(Player player, boolean admin){
|
||||||
|
PlayerAdminPacket packet = new PlayerAdminPacket();
|
||||||
|
packet.admin = admin;
|
||||||
|
packet.id = player.id;
|
||||||
|
player.isAdmin = admin;
|
||||||
|
Net.send(packet, SendMode.tcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleAdministerRequest(Player target, AdminAction action){
|
||||||
|
AdministerRequestPacket packet = new AdministerRequestPacket();
|
||||||
|
packet.id = target.id;
|
||||||
|
packet.action = action;
|
||||||
|
Net.send(packet, SendMode.tcp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleTraceRequest(Player target){
|
||||||
|
if(Net.client()) {
|
||||||
|
handleAdministerRequest(target, AdminAction.trace);
|
||||||
|
}else{
|
||||||
|
ui.traces.show(target, netServer.admins.getTrace(Net.getConnection(target.clientid).address));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Pixmap;
|
|||||||
import com.badlogic.gdx.graphics.Pixmap.Format;
|
import com.badlogic.gdx.graphics.Pixmap.Format;
|
||||||
import com.badlogic.gdx.utils.ByteArray;
|
import com.badlogic.gdx.utils.ByteArray;
|
||||||
import com.badlogic.gdx.utils.TimeUtils;
|
import com.badlogic.gdx.utils.TimeUtils;
|
||||||
|
import io.anuke.mindustry.entities.Player;
|
||||||
import io.anuke.mindustry.game.GameMode;
|
import io.anuke.mindustry.game.GameMode;
|
||||||
import io.anuke.mindustry.resource.Upgrade;
|
import io.anuke.mindustry.resource.Upgrade;
|
||||||
import io.anuke.mindustry.resource.Weapon;
|
import io.anuke.mindustry.resource.Weapon;
|
||||||
@@ -100,7 +101,7 @@ public class NetworkIO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeWorld(int playerID, ByteArray upgrades, OutputStream os){
|
public static void writeWorld(Player player, ByteArray upgrades, OutputStream os){
|
||||||
|
|
||||||
try(DataOutputStream stream = new DataOutputStream(os)){
|
try(DataOutputStream stream = new DataOutputStream(os)){
|
||||||
|
|
||||||
@@ -116,7 +117,8 @@ public class NetworkIO {
|
|||||||
stream.writeInt(state.enemies); //enemy amount
|
stream.writeInt(state.enemies); //enemy amount
|
||||||
|
|
||||||
stream.writeBoolean(state.friendlyFire); //friendly fire state
|
stream.writeBoolean(state.friendlyFire); //friendly fire state
|
||||||
stream.writeInt(playerID); //player remap ID
|
stream.writeInt(player.id); //player remap ID
|
||||||
|
stream.writeBoolean(player.isAdmin);
|
||||||
|
|
||||||
//--INVENTORY--
|
//--INVENTORY--
|
||||||
|
|
||||||
@@ -246,6 +248,7 @@ public class NetworkIO {
|
|||||||
state.friendlyFire = friendlyfire;
|
state.friendlyFire = friendlyfire;
|
||||||
|
|
||||||
int pid = stream.readInt();
|
int pid = stream.readInt();
|
||||||
|
boolean admin = stream.readBoolean();
|
||||||
|
|
||||||
//inventory
|
//inventory
|
||||||
for(int i = 0; i < state.inventory.getItems().length; i ++){
|
for(int i = 0; i < state.inventory.getItems().length; i ++){
|
||||||
@@ -268,6 +271,7 @@ public class NetworkIO {
|
|||||||
|
|
||||||
Entities.clear();
|
Entities.clear();
|
||||||
player.id = pid;
|
player.id = pid;
|
||||||
|
player.isAdmin = admin;
|
||||||
player.add();
|
player.add();
|
||||||
|
|
||||||
//map
|
//map
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import io.anuke.mindustry.io.Version;
|
|||||||
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.resource.Item;
|
import io.anuke.mindustry.resource.Item;
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -371,7 +372,7 @@ public class Packets {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum KickReason{
|
public enum KickReason{
|
||||||
kick, invalidPassword, clientOutdated, serverOutdated
|
kick, invalidPassword, clientOutdated, serverOutdated, banned
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UpgradePacket implements Packet{
|
public static class UpgradePacket implements Packet{
|
||||||
@@ -558,4 +559,97 @@ public class Packets {
|
|||||||
itemid = buffer.get();
|
itemid = buffer.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class NetErrorPacket implements Packet{
|
||||||
|
public String message;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuffer buffer) {
|
||||||
|
buffer.putShort((short)message.getBytes().length);
|
||||||
|
buffer.put(message.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuffer buffer) {
|
||||||
|
short length = buffer.getShort();
|
||||||
|
byte[] bytes = new byte[length];
|
||||||
|
buffer.get(bytes);
|
||||||
|
message = new String(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PlayerAdminPacket implements Packet{
|
||||||
|
public boolean admin;
|
||||||
|
public int id;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuffer buffer) {
|
||||||
|
buffer.put(admin ? (byte)1 : 0);
|
||||||
|
buffer.putInt(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuffer buffer) {
|
||||||
|
admin = buffer.get() == 1;
|
||||||
|
id = buffer.getInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AdministerRequestPacket implements Packet{
|
||||||
|
public AdminAction action;
|
||||||
|
public int id;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuffer buffer) {
|
||||||
|
buffer.put((byte)action.ordinal());
|
||||||
|
buffer.putInt(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuffer buffer) {
|
||||||
|
action = AdminAction.values()[buffer.get()];
|
||||||
|
id = buffer.getInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AdminAction{
|
||||||
|
kick, ban, trace
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TracePacket implements Packet{
|
||||||
|
public TraceInfo info;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(ByteBuffer buffer) {
|
||||||
|
buffer.putInt(info.playerid);
|
||||||
|
buffer.putShort((short)info.ip.getBytes().length);
|
||||||
|
buffer.put(info.ip.getBytes());
|
||||||
|
buffer.put(info.modclient ? (byte)1 : 0);
|
||||||
|
|
||||||
|
buffer.putInt(info.totalBlocksBroken);
|
||||||
|
buffer.putInt(info.structureBlocksBroken);
|
||||||
|
buffer.putInt(info.lastBlockBroken.id);
|
||||||
|
|
||||||
|
buffer.putInt(info.totalBlocksPlaced);
|
||||||
|
buffer.putInt(info.lastBlockPlaced.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(ByteBuffer buffer) {
|
||||||
|
int id = buffer.getInt();
|
||||||
|
short iplen = buffer.getShort();
|
||||||
|
byte[] ipb = new byte[iplen];
|
||||||
|
buffer.get(ipb);
|
||||||
|
|
||||||
|
info = new TraceInfo(new String(ipb));
|
||||||
|
|
||||||
|
info.playerid = id;
|
||||||
|
info.modclient = buffer.get() == 1;
|
||||||
|
info.totalBlocksBroken = buffer.getInt();
|
||||||
|
info.structureBlocksBroken = buffer.getInt();
|
||||||
|
info.lastBlockBroken = Block.getByID(buffer.getInt());
|
||||||
|
info.totalBlocksPlaced = buffer.getInt();
|
||||||
|
info.lastBlockPlaced = Block.getByID(buffer.getInt());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,11 @@ public class Registrator {
|
|||||||
EntitySpawnPacket.class,
|
EntitySpawnPacket.class,
|
||||||
ItemTransferPacket.class,
|
ItemTransferPacket.class,
|
||||||
ItemSetPacket.class,
|
ItemSetPacket.class,
|
||||||
ItemOffloadPacket.class
|
ItemOffloadPacket.class,
|
||||||
|
NetErrorPacket.class,
|
||||||
|
PlayerAdminPacket.class,
|
||||||
|
AdministerRequestPacket.class,
|
||||||
|
TracePacket.class,
|
||||||
};
|
};
|
||||||
private static ObjectIntMap<Class<?>> ids = new ObjectIntMap<>();
|
private static ObjectIntMap<Class<?>> ids = new ObjectIntMap<>();
|
||||||
|
|
||||||
|
|||||||
21
core/src/io/anuke/mindustry/net/TraceInfo.java
Normal file
21
core/src/io/anuke/mindustry/net/TraceInfo.java
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package io.anuke.mindustry.net;
|
||||||
|
|
||||||
|
import io.anuke.mindustry.world.Block;
|
||||||
|
import io.anuke.mindustry.world.blocks.Blocks;
|
||||||
|
|
||||||
|
public class TraceInfo {
|
||||||
|
public int playerid;
|
||||||
|
public String ip;
|
||||||
|
public boolean modclient;
|
||||||
|
|
||||||
|
public int totalBlocksBroken;
|
||||||
|
public int structureBlocksBroken;
|
||||||
|
public Block lastBlockBroken = Blocks.air;
|
||||||
|
|
||||||
|
public int totalBlocksPlaced;
|
||||||
|
public Block lastBlockPlaced = Blocks.air;
|
||||||
|
|
||||||
|
public TraceInfo(String ip){
|
||||||
|
this.ip = ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
65
core/src/io/anuke/mindustry/ui/dialogs/AdminsDialog.java
Normal file
65
core/src/io/anuke/mindustry/ui/dialogs/AdminsDialog.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package io.anuke.mindustry.ui.dialogs;
|
||||||
|
|
||||||
|
import io.anuke.mindustry.entities.Player;
|
||||||
|
import io.anuke.mindustry.net.Net;
|
||||||
|
import io.anuke.mindustry.net.NetConnection;
|
||||||
|
import io.anuke.mindustry.net.NetEvents;
|
||||||
|
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||||
|
import io.anuke.ucore.scene.ui.layout.Table;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
|
public class AdminsDialog extends FloatingDialog {
|
||||||
|
|
||||||
|
public AdminsDialog(){
|
||||||
|
super("$text.server.admins");
|
||||||
|
|
||||||
|
addCloseButton();
|
||||||
|
|
||||||
|
setup();
|
||||||
|
shown(this::setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup(){
|
||||||
|
content().clear();
|
||||||
|
|
||||||
|
if(gwt) return;
|
||||||
|
|
||||||
|
float w = 400f, h = 80f;
|
||||||
|
|
||||||
|
Table table = new Table();
|
||||||
|
|
||||||
|
ScrollPane pane = new ScrollPane(table, "clear");
|
||||||
|
pane.setFadeScrollBars(false);
|
||||||
|
|
||||||
|
if(netServer.admins.getAdmins().size == 0){
|
||||||
|
table.add("$text.server.admins.none");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String ip : netServer.admins.getAdmins()){
|
||||||
|
Table res = new Table("button");
|
||||||
|
res.margin(14f);
|
||||||
|
|
||||||
|
res.labelWrap("[LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f);
|
||||||
|
res.add().growX();
|
||||||
|
res.addImageButton("icon-cancel", 14*3, () -> {
|
||||||
|
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
|
||||||
|
netServer.admins.unAdminPlayer(ip);
|
||||||
|
for(Player player : playerGroup.all()){
|
||||||
|
NetConnection c = Net.getConnection(player.clientid);
|
||||||
|
if(c != null){
|
||||||
|
NetEvents.handleAdminSet(player, false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setup();
|
||||||
|
});
|
||||||
|
}).size(h).pad(-14f);
|
||||||
|
|
||||||
|
table.add(res).width(w).height(h);
|
||||||
|
table.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
content().add(pane);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
core/src/io/anuke/mindustry/ui/dialogs/BansDialog.java
Normal file
55
core/src/io/anuke/mindustry/ui/dialogs/BansDialog.java
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package io.anuke.mindustry.ui.dialogs;
|
||||||
|
|
||||||
|
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||||
|
import io.anuke.ucore.scene.ui.layout.Table;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
|
public class BansDialog extends FloatingDialog {
|
||||||
|
|
||||||
|
public BansDialog(){
|
||||||
|
super("$text.server.bans");
|
||||||
|
|
||||||
|
addCloseButton();
|
||||||
|
|
||||||
|
setup();
|
||||||
|
|
||||||
|
shown(this::setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setup(){
|
||||||
|
content().clear();
|
||||||
|
|
||||||
|
if(gwt) return;
|
||||||
|
|
||||||
|
float w = 400f, h = 80f;
|
||||||
|
|
||||||
|
Table table = new Table();
|
||||||
|
|
||||||
|
ScrollPane pane = new ScrollPane(table, "clear");
|
||||||
|
pane.setFadeScrollBars(false);
|
||||||
|
|
||||||
|
if(netServer.admins.getBanned().size == 0){
|
||||||
|
table.add("$text.server.bans.none");
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String ip : netServer.admins.getBanned()){
|
||||||
|
Table res = new Table("button");
|
||||||
|
res.margin(14f);
|
||||||
|
|
||||||
|
res.labelWrap("IP: [LIGHT_GRAY]" + ip + "\n[]Name: [LIGHT_GRAY]" + netServer.admins.getLastName(ip)).width(w - h - 24f);
|
||||||
|
res.add().growX();
|
||||||
|
res.addImageButton("icon-cancel", 14*3, () -> {
|
||||||
|
ui.showConfirm("$text.confirm", "$text.confirmunban", () -> {
|
||||||
|
netServer.admins.unbanPlayer(ip);
|
||||||
|
setup();
|
||||||
|
});
|
||||||
|
}).size(h).pad(-14f);
|
||||||
|
|
||||||
|
table.add(res).width(w).height(h);
|
||||||
|
table.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
content().add(pane);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,6 @@ import java.io.IOException;
|
|||||||
import static io.anuke.mindustry.Vars.player;
|
import static io.anuke.mindustry.Vars.player;
|
||||||
import static io.anuke.mindustry.Vars.ui;
|
import static io.anuke.mindustry.Vars.ui;
|
||||||
|
|
||||||
//TODO add port specification
|
|
||||||
public class HostDialog extends FloatingDialog{
|
public class HostDialog extends FloatingDialog{
|
||||||
float w = 300;
|
float w = 300;
|
||||||
|
|
||||||
@@ -31,13 +30,13 @@ public class HostDialog extends FloatingDialog{
|
|||||||
Settings.put("name", text);
|
Settings.put("name", text);
|
||||||
Settings.save();
|
Settings.save();
|
||||||
ui.listfrag.rebuild();
|
ui.listfrag.rebuild();
|
||||||
}).grow().pad(8);
|
}).grow().pad(8).get().setMaxLength(40);
|
||||||
|
|
||||||
ImageButton button = t.addImageButton("white", 40, () -> {
|
ImageButton button = t.addImageButton("white", 40, () -> {
|
||||||
new ColorPickDialog().show(color -> {
|
new ColorPickDialog().show(color -> {
|
||||||
player.color.set(color);
|
player.color.set(color);
|
||||||
Settings.putInt("color", Color.rgba8888(color));
|
Settings.putInt("color", Color.rgba8888(color));
|
||||||
Settings.save();;
|
Settings.save();
|
||||||
});
|
});
|
||||||
}).size(50f, 54f).get();
|
}).size(50f, 54f).get();
|
||||||
button.update(() -> button.getStyle().imageUpColor = player.getColor());
|
button.update(() -> button.getStyle().imageUpColor = player.getColor());
|
||||||
@@ -50,6 +49,7 @@ public class HostDialog extends FloatingDialog{
|
|||||||
Timers.runTask(5f, () -> {
|
Timers.runTask(5f, () -> {
|
||||||
try{
|
try{
|
||||||
Net.host(Vars.port);
|
Net.host(Vars.port);
|
||||||
|
player.isAdmin = true;
|
||||||
}catch (IOException e){
|
}catch (IOException e){
|
||||||
ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false)));
|
ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false)));
|
||||||
}
|
}
|
||||||
@@ -58,19 +58,4 @@ public class HostDialog extends FloatingDialog{
|
|||||||
});
|
});
|
||||||
}).width(w).height(70f);
|
}).width(w).height(70f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
showTextInput("$text.hostserver", "$text.server.port", Vars.port + "", new DigitsOnlyFilter(), text -> {
|
|
||||||
int result = Strings.parseInt(text);
|
|
||||||
if(result == Integer.MIN_VALUE || result >= 65535){
|
|
||||||
ui.showError("$text.server.invalidport");
|
|
||||||
}else{
|
|
||||||
try{
|
|
||||||
Net.host(result);
|
|
||||||
}catch (IOException e){
|
|
||||||
ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
setupRemote();
|
setupRemote();
|
||||||
refreshRemote();
|
refreshRemote();
|
||||||
}else{
|
}else{
|
||||||
//renaming.port = Strings.parseInt(Settings.getString("port"));
|
|
||||||
renaming.ip = Settings.getString("ip");
|
renaming.ip = Settings.getString("ip");
|
||||||
saveServers();
|
saveServers();
|
||||||
setupRemote();
|
setupRemote();
|
||||||
@@ -85,7 +84,7 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
|
|
||||||
TextButton button = buttons[0] = remote.addButton("[accent]"+server.ip, "clear", () -> {
|
TextButton button = buttons[0] = remote.addButton("[accent]"+server.ip, "clear", () -> {
|
||||||
if(!buttons[0].childrenPressed()) connect(server.ip, Vars.port);
|
if(!buttons[0].childrenPressed()) connect(server.ip, Vars.port);
|
||||||
}).width(w).height(120f).pad(4f).get();
|
}).width(w).height(140f).pad(4f).get();
|
||||||
|
|
||||||
button.getLabel().setWrap(true);
|
button.getLabel().setWrap(true);
|
||||||
|
|
||||||
@@ -134,10 +133,14 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
Net.pingHost(server.ip, server.port, host -> {
|
Net.pingHost(server.ip, server.port, host -> {
|
||||||
server.content.clear();
|
server.content.clear();
|
||||||
|
|
||||||
server.content.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).pad(4);
|
server.content.add("[lightgray]" + Bundles.format("text.server.hostname", host.name)).left();
|
||||||
server.content.row();
|
server.content.row();
|
||||||
server.content.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) :
|
server.content.add("[lightgray]" + (host.players != 1 ? Bundles.format("text.players", host.players) :
|
||||||
Bundles.format("text.players.single", host.players)));
|
Bundles.format("text.players.single", host.players))).left();
|
||||||
|
server.content.row();
|
||||||
|
server.content.add("[lightgray]" + Bundles.format("text.save.map", host.mapname)).left();
|
||||||
|
server.content.row();
|
||||||
|
server.content.add("[lightgray]" + Bundles.format("text.save.wave", host.wave)).left();
|
||||||
}, e -> {
|
}, e -> {
|
||||||
server.content.clear();
|
server.content.clear();
|
||||||
server.content.add("$text.host.invalid");
|
server.content.add("$text.host.invalid");
|
||||||
@@ -175,7 +178,7 @@ public class JoinDialog extends FloatingDialog {
|
|||||||
Vars.player.name = text;
|
Vars.player.name = text;
|
||||||
Settings.put("name", text);
|
Settings.put("name", text);
|
||||||
Settings.save();
|
Settings.save();
|
||||||
}).grow().pad(8);
|
}).grow().pad(8).get().setMaxLength(40);
|
||||||
|
|
||||||
ImageButton button = t.addImageButton("white", 40, () -> {
|
ImageButton button = t.addImageButton("white", 40, () -> {
|
||||||
new ColorPickDialog().show(color -> {
|
new ColorPickDialog().show(color -> {
|
||||||
|
|||||||
@@ -32,18 +32,18 @@ public class SettingsMenuDialog extends SettingsDialog{
|
|||||||
public SettingsMenuDialog(){
|
public SettingsMenuDialog(){
|
||||||
setStyle(Core.skin.get("dialog", WindowStyle.class));
|
setStyle(Core.skin.get("dialog", WindowStyle.class));
|
||||||
|
|
||||||
hidden(()->{
|
hidden(() -> {
|
||||||
if(!state.is(State.menu)){
|
if(!state.is(State.menu)){
|
||||||
if(!wasPaused || Net.active())
|
if(!wasPaused || Net.active())
|
||||||
state.set(State.playing);
|
state.set(State.playing);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
shown(()->{
|
shown(() -> {
|
||||||
if(!state.is(State.menu)){
|
if(!state.is(State.menu)){
|
||||||
wasPaused = state.is(State.paused);
|
wasPaused = state.is(State.paused);
|
||||||
if(menu.getScene() != null){
|
if(ui.paused.getScene() != null){
|
||||||
wasPaused = ((PausedDialog)menu).wasPaused;
|
wasPaused = ui.paused.wasPaused;
|
||||||
}
|
}
|
||||||
if(!Net.active()) state.set(State.paused);
|
if(!Net.active()) state.set(State.paused);
|
||||||
ui.paused.hide();
|
ui.paused.hide();
|
||||||
|
|||||||
53
core/src/io/anuke/mindustry/ui/dialogs/TraceDialog.java
Normal file
53
core/src/io/anuke/mindustry/ui/dialogs/TraceDialog.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package io.anuke.mindustry.ui.dialogs;
|
||||||
|
|
||||||
|
import io.anuke.mindustry.entities.Player;
|
||||||
|
import io.anuke.mindustry.net.TraceInfo;
|
||||||
|
import io.anuke.ucore.scene.ui.layout.Table;
|
||||||
|
import io.anuke.ucore.util.Bundles;
|
||||||
|
|
||||||
|
public class TraceDialog extends FloatingDialog {
|
||||||
|
|
||||||
|
public TraceDialog(){
|
||||||
|
super("$text.trace");
|
||||||
|
|
||||||
|
addCloseButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show(Player player, TraceInfo info){
|
||||||
|
content().clear();
|
||||||
|
|
||||||
|
Table table = new Table("button");
|
||||||
|
table.margin(14);
|
||||||
|
table.defaults().pad(1);
|
||||||
|
|
||||||
|
table.defaults().left();
|
||||||
|
table.add(Bundles.format("text.trace.playername", player.name));
|
||||||
|
table.row();
|
||||||
|
table.add(Bundles.format("text.trace.ip", info.ip));
|
||||||
|
table.row();
|
||||||
|
table.add(Bundles.format("text.trace.modclient", info.modclient));
|
||||||
|
table.row();
|
||||||
|
|
||||||
|
table.add().pad(5);
|
||||||
|
table.row();
|
||||||
|
|
||||||
|
table.add(Bundles.format("text.trace.totalblocksbroken", info.totalBlocksBroken));
|
||||||
|
table.row();
|
||||||
|
table.add(Bundles.format("text.trace.structureblocksbroken", info.structureBlocksBroken));
|
||||||
|
table.row();
|
||||||
|
table.add(Bundles.format("text.trace.lastblockbroken", info.lastBlockBroken.formalName));
|
||||||
|
table.row();
|
||||||
|
|
||||||
|
table.add().pad(5);
|
||||||
|
table.row();
|
||||||
|
|
||||||
|
table.add(Bundles.format("text.trace.totalblocksplaced", info.totalBlocksPlaced));
|
||||||
|
table.row();
|
||||||
|
table.add(Bundles.format("text.trace.lastblockplaced", info.lastBlockPlaced.formalName));
|
||||||
|
table.row();
|
||||||
|
|
||||||
|
content().add(table);
|
||||||
|
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,13 +24,14 @@ public class BackgroundFragment implements Fragment {
|
|||||||
Core.batch.draw(back, w/2 - back.getRegionWidth()*backscl/2 +240f, h/2 - back.getRegionHeight()*backscl/2 + 250f,
|
Core.batch.draw(back, w/2 - back.getRegionWidth()*backscl/2 +240f, h/2 - back.getRegionHeight()*backscl/2 + 250f,
|
||||||
back.getRegionWidth()*backscl, back.getRegionHeight()*backscl);
|
back.getRegionWidth()*backscl, back.getRegionHeight()*backscl);
|
||||||
|
|
||||||
float logoscl = (int)Unit.dp.scl(7);
|
boolean portrait = Gdx.graphics.getWidth() < Gdx.graphics.getHeight();
|
||||||
|
float logoscl = (int)Unit.dp.scl(7) * (portrait ? 5f/7f : 1f);
|
||||||
TextureRegion logo = Core.skin.getRegion("logotext");
|
TextureRegion logo = Core.skin.getRegion("logotext");
|
||||||
float logow = logo.getRegionWidth()*logoscl;
|
float logow = logo.getRegionWidth()*logoscl;
|
||||||
float logoh = logo.getRegionHeight()*logoscl;
|
float logoh = logo.getRegionHeight()*logoscl;
|
||||||
|
|
||||||
Draw.color();
|
Draw.color();
|
||||||
Core.batch.draw(logo, w/2 - logow/2, h - logoh + 15, logow, logoh);
|
Core.batch.draw(logo, w/2 - logow/2, h - logoh + 15 + (portrait ? -Unit.dp.scl(30f) : 0f), logow, logoh);
|
||||||
}).visible(() -> state.is(State.menu)).grow();
|
}).visible(() -> state.is(State.menu)).grow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,11 +57,22 @@ public class DebugFragment implements Fragment {
|
|||||||
row();
|
row();
|
||||||
new button("wave", () -> state.wavetime = 0f);
|
new button("wave", () -> state.wavetime = 0f);
|
||||||
row();
|
row();
|
||||||
|
new button("time 0", () -> Timers.resetTime(0f));
|
||||||
|
row();
|
||||||
|
new button("time max", () -> Timers.resetTime(1080000 - 60*10));
|
||||||
|
row();
|
||||||
|
new button("clear", () -> {
|
||||||
|
enemyGroup.clear();
|
||||||
|
state.enemies = 0;
|
||||||
|
netClient.clearRecieved();
|
||||||
|
});
|
||||||
|
row();
|
||||||
new button("spawn", () -> {
|
new button("spawn", () -> {
|
||||||
for(int i = 0; i < 30; i ++){
|
for(int i = 0; i < 30; i ++){
|
||||||
new Enemy(EnemyTypes.healer).set(player.x + Mathf.range(50f), player.y + Mathf.range(50f)).add();
|
new Enemy(EnemyTypes.healer).set(player.x + Mathf.range(50f), player.y + Mathf.range(50f)).add();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
row();
|
||||||
}}.end();
|
}}.end();
|
||||||
|
|
||||||
row();
|
row();
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import static io.anuke.mindustry.Vars.*;
|
|||||||
public class MenuFragment implements Fragment{
|
public class MenuFragment implements Fragment{
|
||||||
|
|
||||||
public void build(){
|
public void build(){
|
||||||
if(!android){
|
new table(){{
|
||||||
//menu table
|
visible(() -> state.is(State.menu));
|
||||||
new table(){{
|
|
||||||
|
if(!android){
|
||||||
new table(){{
|
new table(){{
|
||||||
PressGroup group = new PressGroup();
|
PressGroup group = new PressGroup();
|
||||||
|
|
||||||
@@ -53,37 +53,32 @@ public class MenuFragment implements Fragment{
|
|||||||
}
|
}
|
||||||
get().margin(16);
|
get().margin(16);
|
||||||
}}.end();
|
}}.end();
|
||||||
|
|
||||||
visible(() -> state.is(State.menu));
|
}else {
|
||||||
}}.end();
|
new table() {{
|
||||||
}else{
|
|
||||||
new table(){{
|
|
||||||
new table(){{
|
|
||||||
defaults().size(120f).pad(5);
|
defaults().size(120f).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);
|
||||||
|
|
||||||
new imagebutton("icon-tutorial", isize, () -> control.playMap(world.maps().getMap("tutorial"))).text("$text.tutorial").padTop(4f);
|
new imagebutton("icon-tutorial", isize, () -> control.playMap(world.maps().getMap("tutorial"))).text("$text.tutorial").padTop(4f);
|
||||||
|
|
||||||
new imagebutton("icon-load", isize, ui.load::show).text("$text.load").padTop(4f);
|
new imagebutton("icon-load", isize, ui.load::show).text("$text.load").padTop(4f);
|
||||||
|
|
||||||
new imagebutton("icon-add", isize, ui.join::show).text("$text.joingame").padTop(4f);
|
new imagebutton("icon-add", isize, ui.join::show).text("$text.joingame").padTop(4f);
|
||||||
|
|
||||||
row();
|
row();
|
||||||
|
|
||||||
new imagebutton("icon-editor", isize, ui.editor::show).text("$text.editor").padTop(4f);
|
new imagebutton("icon-editor", isize, ui.editor::show).text("$text.editor").padTop(4f);
|
||||||
|
|
||||||
new imagebutton("icon-tools", isize, ui.settings::show).text("$text.settings").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);
|
new imagebutton("icon-info", isize, ui.about::show).text("$text.about.button").padTop(4f);
|
||||||
|
|
||||||
new imagebutton("icon-donate", isize, Platform.instance::openDonations).text("$text.donate").padTop(4f);
|
new imagebutton("icon-donate", isize, Platform.instance::openDonations).text("$text.donate").padTop(4f);
|
||||||
|
|
||||||
visible(() -> state.is(State.menu));
|
|
||||||
}}.end();
|
}}.end();
|
||||||
}}.end();
|
}
|
||||||
}
|
}}.end();
|
||||||
|
|
||||||
//extra icons in top right
|
//extra icons in top right
|
||||||
new table(){{
|
new table(){{
|
||||||
|
|||||||
@@ -214,6 +214,7 @@ public class PlacementFragment implements Fragment{
|
|||||||
breaktable.getParent().swapActor(breaktable, next);
|
breaktable.getParent().swapActor(breaktable, next);
|
||||||
|
|
||||||
if(!show){
|
if(!show){
|
||||||
|
control.input().breakMode = PlaceMode.none;
|
||||||
breaktable.actions(Actions.translateBy(-breaktable.getWidth() - 5, 0, dur, in), Actions.call(() -> shown = false));
|
breaktable.actions(Actions.translateBy(-breaktable.getWidth() - 5, 0, dur, in), Actions.call(() -> shown = false));
|
||||||
}else{
|
}else{
|
||||||
shown = true;
|
shown = true;
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
package io.anuke.mindustry.ui.fragments;
|
package io.anuke.mindustry.ui.fragments;
|
||||||
|
|
||||||
|
import io.anuke.mindustry.Vars;
|
||||||
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.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
|
import io.anuke.mindustry.net.NetConnection;
|
||||||
import io.anuke.mindustry.net.NetEvents;
|
import io.anuke.mindustry.net.NetEvents;
|
||||||
|
import io.anuke.mindustry.net.Packets.AdminAction;
|
||||||
import io.anuke.mindustry.net.Packets.KickReason;
|
import io.anuke.mindustry.net.Packets.KickReason;
|
||||||
import io.anuke.mindustry.ui.BorderImage;
|
import io.anuke.mindustry.ui.BorderImage;
|
||||||
import io.anuke.ucore.core.Inputs;
|
import io.anuke.ucore.core.Inputs;
|
||||||
import io.anuke.ucore.graphics.Draw;
|
import io.anuke.ucore.graphics.Draw;
|
||||||
import io.anuke.ucore.scene.Element;
|
import io.anuke.ucore.scene.Element;
|
||||||
|
import io.anuke.ucore.scene.builders.button;
|
||||||
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.scene.event.Touchable;
|
||||||
import io.anuke.ucore.scene.ui.ScrollPane;
|
import io.anuke.ucore.scene.ui.ScrollPane;
|
||||||
import io.anuke.ucore.scene.ui.layout.Stack;
|
import io.anuke.ucore.scene.ui.layout.Stack;
|
||||||
import io.anuke.ucore.scene.ui.layout.Table;
|
import io.anuke.ucore.scene.ui.layout.Table;
|
||||||
@@ -40,10 +45,20 @@ public class PlayerListFragment implements Fragment{
|
|||||||
row();
|
row();
|
||||||
new table("pane"){{
|
new table("pane"){{
|
||||||
margin(12f);
|
margin(12f);
|
||||||
|
|
||||||
get().addCheck("$text.server.friendlyfire", b -> {
|
get().addCheck("$text.server.friendlyfire", b -> {
|
||||||
state.friendlyFire = b;
|
state.friendlyFire = b;
|
||||||
NetEvents.handleFriendlyFireChange(b);
|
NetEvents.handleFriendlyFireChange(b);
|
||||||
}).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client());
|
}).growX().update(i -> i.setChecked(state.friendlyFire)).disabled(b -> Net.client()).padRight(5);
|
||||||
|
|
||||||
|
new button("$text.server.bans", () -> {
|
||||||
|
ui.bans.show();
|
||||||
|
}).padTop(-12).padBottom(-12).fillY().cell.disabled(b -> Net.client());
|
||||||
|
|
||||||
|
new button("$text.server.admins", () -> {
|
||||||
|
ui.admins.show();
|
||||||
|
}).padTop(-12).padBottom(-12).padRight(-12).fillY().cell.disabled(b -> Net.client());
|
||||||
|
|
||||||
}}.pad(10f).growX().end();
|
}}.pad(10f).growX().end();
|
||||||
}}.end();
|
}}.end();
|
||||||
|
|
||||||
@@ -69,9 +84,13 @@ public class PlayerListFragment implements Fragment{
|
|||||||
public void rebuild(){
|
public void rebuild(){
|
||||||
content.clear();
|
content.clear();
|
||||||
|
|
||||||
float h = 60f;
|
float h = 74f;
|
||||||
|
|
||||||
for(Player player : playerGroup.all()){
|
for(Player player : playerGroup.all()){
|
||||||
|
NetConnection connection = gwt ? null : Net.getConnection(player.clientid);
|
||||||
|
|
||||||
|
if(connection == null && Net.server() && !player.isLocal) continue;
|
||||||
|
|
||||||
Table button = new Table("button");
|
Table button = new Table("button");
|
||||||
button.left();
|
button.left();
|
||||||
button.margin(5).marginBottom(10);
|
button.margin(5).marginBottom(10);
|
||||||
@@ -93,19 +112,68 @@ public class PlayerListFragment implements Fragment{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
button.add(stack).size(h);
|
button.add(stack).size(h);
|
||||||
button.add("[#" + player.getColor().toString().toUpperCase() + "]" + player.name).pad(10);
|
button.labelWrap("[#" + player.getColor().toString().toUpperCase() + "]" + player.name).width(170f).pad(10);
|
||||||
button.add().grow();
|
button.add().grow();
|
||||||
|
|
||||||
if(Net.server() && !player.isLocal){
|
button.addImage("icon-admin").size(14*2).visible(() -> player.isAdmin && !(!player.isLocal && Net.server())).padRight(5);
|
||||||
|
|
||||||
|
if((Net.server() || Vars.player.isAdmin) && !player.isLocal && (!player.isAdmin || Net.server())){
|
||||||
button.add().growY();
|
button.add().growY();
|
||||||
button.addImageButton("icon-cancel", 14*3, () ->
|
|
||||||
Net.kickConnection(player.clientid, KickReason.kick)
|
float bs = (h + 14)/2f;
|
||||||
).pad(-5).padBottom(-10).size(h+10, h+14);
|
|
||||||
|
button.table(t -> {
|
||||||
|
t.defaults().size(bs - 1, bs + 3);
|
||||||
|
|
||||||
|
t.addImageButton("icon-ban", 14*2, () -> {
|
||||||
|
ui.showConfirm("$text.confirm", "$text.confirmban", () -> {
|
||||||
|
if(Net.server()) {
|
||||||
|
netServer.admins.banPlayer(connection.address);
|
||||||
|
Net.kickConnection(player.clientid, KickReason.banned);
|
||||||
|
}else{
|
||||||
|
NetEvents.handleAdministerRequest(player, AdminAction.ban);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).padBottom(-5.1f);
|
||||||
|
|
||||||
|
t.addImageButton("icon-cancel", 14*2, () -> {
|
||||||
|
if(Net.server()) {
|
||||||
|
Net.kickConnection(player.clientid, KickReason.kick);
|
||||||
|
}else{
|
||||||
|
NetEvents.handleAdministerRequest(player, AdminAction.kick);
|
||||||
|
}
|
||||||
|
}).padBottom(-5.1f);
|
||||||
|
|
||||||
|
t.row();
|
||||||
|
|
||||||
|
t.addImageButton("icon-admin", "toggle", 14*2, () -> {
|
||||||
|
if(Net.client()) return;
|
||||||
|
|
||||||
|
if(netServer.admins.isAdmin(connection.address)){
|
||||||
|
ui.showConfirm("$text.confirm", "$text.confirmunadmin", () -> {
|
||||||
|
netServer.admins.unAdminPlayer(connection.address);
|
||||||
|
NetEvents.handleAdminSet(player, false);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
ui.showConfirm("$text.confirm", "$text.confirmadmin", () -> {
|
||||||
|
netServer.admins.adminPlayer(connection.address);
|
||||||
|
NetEvents.handleAdminSet(player, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).update(b ->{
|
||||||
|
b.setChecked(player.isAdmin);
|
||||||
|
b.setDisabled(Net.client());
|
||||||
|
}).get().setTouchable(() -> Net.client() ? Touchable.disabled : Touchable.enabled);
|
||||||
|
|
||||||
|
t.addImageButton("icon-zoom-small", 14*2, () -> NetEvents.handleTraceRequest(player));
|
||||||
|
|
||||||
|
}).padRight(12).padTop(-5).padLeft(0).padBottom(-10).size(bs + 10f, bs);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
content.add(button).padBottom(-5).width(350f);
|
content.add(button).padBottom(-6).width(350f).maxHeight(h + 14);
|
||||||
content.row();
|
content.row();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
166
core/src/io/anuke/mindustry/world/Placement.java
Normal file
166
core/src/io/anuke/mindustry/world/Placement.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
package io.anuke.mindustry.world;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.math.Rectangle;
|
||||||
|
import com.badlogic.gdx.math.Vector2;
|
||||||
|
import com.badlogic.gdx.utils.Array;
|
||||||
|
import io.anuke.mindustry.entities.Player;
|
||||||
|
import io.anuke.mindustry.game.SpawnPoint;
|
||||||
|
import io.anuke.mindustry.graphics.Fx;
|
||||||
|
import io.anuke.mindustry.resource.ItemStack;
|
||||||
|
import io.anuke.mindustry.resource.Recipe;
|
||||||
|
import io.anuke.mindustry.resource.Recipes;
|
||||||
|
import io.anuke.mindustry.world.blocks.Blocks;
|
||||||
|
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||||
|
import io.anuke.ucore.core.Effects;
|
||||||
|
import io.anuke.ucore.core.Sounds;
|
||||||
|
import io.anuke.ucore.entities.Entities;
|
||||||
|
import io.anuke.ucore.entities.SolidEntity;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
|
public class Placement {
|
||||||
|
private static final Rectangle rect = new Rectangle();
|
||||||
|
|
||||||
|
/**Returns block type that was broken, or null if unsuccesful.*/
|
||||||
|
public static Block breakBlock(int x, int y, boolean effect, boolean sound){
|
||||||
|
Tile tile = world.tile(x, y);
|
||||||
|
|
||||||
|
if(tile == null) return null;
|
||||||
|
|
||||||
|
Block block = tile.isLinked() ? tile.getLinked().block() : tile.block();
|
||||||
|
Recipe result = Recipes.getByResult(block);
|
||||||
|
|
||||||
|
if(result != null){
|
||||||
|
for(ItemStack stack : result.requirements){
|
||||||
|
state.inventory.addItem(stack.item, (int)(stack.amount * breakDropAmount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tile.block().drops != null){
|
||||||
|
state.inventory.addItem(tile.block().drops.item, tile.block().drops.amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sound) Sounds.play("break");
|
||||||
|
|
||||||
|
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
||||||
|
tile.setBlock(Blocks.air);
|
||||||
|
if(effect) Effects.effect(Fx.breakBlock, tile.worldx(), tile.worldy());
|
||||||
|
}else{
|
||||||
|
Tile target = tile.isLinked() ? tile.getLinked() : tile;
|
||||||
|
Array<Tile> removals = target.getLinkedTiles();
|
||||||
|
for(Tile toremove : removals){
|
||||||
|
//note that setting a new block automatically unlinks it
|
||||||
|
toremove.setBlock(Blocks.air);
|
||||||
|
if(effect) Effects.effect(Fx.breakBlock, toremove.worldx(), toremove.worldy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){
|
||||||
|
Tile tile = world.tile(x, y);
|
||||||
|
|
||||||
|
//just in case
|
||||||
|
if(tile == null) return;
|
||||||
|
|
||||||
|
tile.setBlock(result, rotation);
|
||||||
|
|
||||||
|
if(result.isMultiblock()){
|
||||||
|
int offsetx = -(result.width-1)/2;
|
||||||
|
int offsety = -(result.height-1)/2;
|
||||||
|
|
||||||
|
for(int dx = 0; dx < result.width; dx ++){
|
||||||
|
for(int dy = 0; dy < result.height; dy ++){
|
||||||
|
int worldx = dx + offsetx + x;
|
||||||
|
int worldy = dy + offsety + y;
|
||||||
|
if(!(worldx == x && worldy == y)){
|
||||||
|
Tile toplace = world.tile(worldx, worldy);
|
||||||
|
if(toplace != null)
|
||||||
|
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(effects) Effects.effect(Fx.place, worldx * tilesize, worldy * tilesize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if(effects) Effects.effect(Fx.place, x * tilesize, y * tilesize);
|
||||||
|
|
||||||
|
if(effects && sound) Sounds.play("place");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean validPlace(int x, int y, Block type){
|
||||||
|
for(int i = 0; i < world.getSpawns().size; i ++){
|
||||||
|
SpawnPoint spawn = world.getSpawns().get(i);
|
||||||
|
if(Vector2.dst(x * tilesize, y * tilesize, spawn.start.worldx(), spawn.start.worldy()) < enemyspawnspace){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Recipe recipe = Recipes.getByResult(type);
|
||||||
|
|
||||||
|
if(recipe == null || !state.inventory.hasItems(recipe.requirements)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.setSize(type.width * tilesize, type.height * tilesize);
|
||||||
|
Vector2 offset = type.getPlaceOffset();
|
||||||
|
rect.setCenter(offset.x + x * tilesize, offset.y + y * tilesize);
|
||||||
|
|
||||||
|
synchronized (Entities.entityLock) {
|
||||||
|
for (SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)) {
|
||||||
|
if (e == null) continue; //not sure why this happens?
|
||||||
|
Rectangle rect = e.hitbox.getRect(e.x, e.y);
|
||||||
|
|
||||||
|
if (Placement.rect.overlaps(rect)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type.solid || type.solidifes) {
|
||||||
|
for (Player player : playerGroup.all()) {
|
||||||
|
if (!player.isAndroid && rect.overlaps(player.hitbox.getRect(player.x, player.y))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile tile = world.tile(x, y);
|
||||||
|
|
||||||
|
if(tile == null || (isSpawnPoint(tile) && (type.solidifes || type.solid))) return false;
|
||||||
|
|
||||||
|
if(type.isMultiblock()){
|
||||||
|
int offsetx = -(type.width-1)/2;
|
||||||
|
int offsety = -(type.height-1)/2;
|
||||||
|
for(int dx = 0; dx < type.width; dx ++){
|
||||||
|
for(int dy = 0; dy < type.height; dy ++){
|
||||||
|
Tile other = world.tile(x + dx + offsetx, y + dy + offsety);
|
||||||
|
if(other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) || isSpawnPoint(other)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}else {
|
||||||
|
return tile.block() != type
|
||||||
|
&& (type.canReplace(tile.block()) || tile.block().alwaysReplace)
|
||||||
|
&& tile.block().isMultiblock() == type.isMultiblock() || tile.block() == Blocks.air;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSpawnPoint(Tile tile){
|
||||||
|
return tile != null && tile.x == world.getCore().x && tile.y == world.getCore().y - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean validBreak(int x, int y){
|
||||||
|
Tile tile = world.tile(x, y);
|
||||||
|
|
||||||
|
if(tile == null || tile.block() == ProductionBlocks.core) return false;
|
||||||
|
|
||||||
|
if(tile.isLinked() && tile.getLinked().block() == ProductionBlocks.core){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tile.breakable();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,13 +43,14 @@ public class LaserTurret extends PowerTurret{
|
|||||||
@Override
|
@Override
|
||||||
public void drawLayer2(Tile tile){
|
public void drawLayer2(Tile tile){
|
||||||
TurretEntity entity = tile.entity();
|
TurretEntity entity = tile.entity();
|
||||||
|
Enemy enemy = entity.target;
|
||||||
|
|
||||||
if(entity.target != null &&
|
if(enemy != null &&
|
||||||
Angles.angleDist(entity.rotation, Angles.angle(tile.drawx(), tile.drawy(), entity.target.x, entity.target.y)) <= cone){
|
Angles.angleDist(entity.rotation, Angles.angle(tile.drawx(), tile.drawy(), enemy.x, enemy.y)) <= cone){
|
||||||
float len = 4f;
|
float len = 4f;
|
||||||
|
|
||||||
float x = tile.drawx() + Angles.trnsx(entity.rotation, len), y = tile.drawy() + Angles.trnsy(entity.rotation, len);
|
float x = tile.drawx() + Angles.trnsx(entity.rotation, len), y = tile.drawy() + Angles.trnsy(entity.rotation, len);
|
||||||
float x2 = entity.target.x, y2 = entity.target.y;
|
float x2 = enemy.x, y2 = enemy.y;
|
||||||
|
|
||||||
float lighten = (MathUtils.sin(Timers.time()/1.2f) + 1f) / 10f;
|
float lighten = (MathUtils.sin(Timers.time()/1.2f) + 1f) / 10f;
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.anuke.mindustry.world.blocks.types.defense;
|
|||||||
import com.badlogic.gdx.graphics.Color;
|
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.entities.TileEntity;
|
||||||
import io.anuke.mindustry.world.Layer;
|
import io.anuke.mindustry.world.Layer;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.ucore.core.Timers;
|
import io.anuke.ucore.core.Timers;
|
||||||
@@ -80,9 +81,10 @@ public class RepairTurret extends PowerTurret{
|
|||||||
@Override
|
@Override
|
||||||
public void drawLayer2(Tile tile){
|
public void drawLayer2(Tile tile){
|
||||||
PowerTurretEntity entity = tile.entity();
|
PowerTurretEntity entity = tile.entity();
|
||||||
|
TileEntity target = entity.blockTarget;
|
||||||
|
|
||||||
if(entity.power >= powerUsed && entity.blockTarget != null && Angles.angleDist(entity.angleTo(entity.blockTarget), entity.rotation) < 10){
|
if(entity.power >= powerUsed && target != null && Angles.angleDist(entity.angleTo(target), entity.rotation) < 10){
|
||||||
Tile targetTile = entity.blockTarget.tile;
|
Tile targetTile = target.tile;
|
||||||
float len = 4f;
|
float len = 4f;
|
||||||
|
|
||||||
float x = tile.drawx() + Angles.trnsx(entity.rotation, len), y = tile.drawy() + Angles.trnsy(entity.rotation, len);
|
float x = tile.drawx() + Angles.trnsx(entity.rotation, len), y = tile.drawy() + Angles.trnsy(entity.rotation, len);
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ public class Junction extends Block{
|
|||||||
return new JunctionEntity();
|
return new JunctionEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Array<Object> getDebugInfo(Tile tile){
|
public Array<Object> getDebugInfo(Tile tile){
|
||||||
JunctionEntity entity = tile.entity();
|
JunctionEntity entity = tile.entity();
|
||||||
Array<Object> arr = super.getDebugInfo(tile);
|
Array<Object> arr = super.getDebugInfo(tile);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.anuke.mindustry.world.blocks.types.distribution;
|
package io.anuke.mindustry.world.blocks.types.distribution;
|
||||||
|
|
||||||
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.NumberUtils;
|
import com.badlogic.gdx.utils.NumberUtils;
|
||||||
import io.anuke.mindustry.entities.TileEntity;
|
import io.anuke.mindustry.entities.TileEntity;
|
||||||
import io.anuke.mindustry.resource.Item;
|
import io.anuke.mindustry.resource.Item;
|
||||||
@@ -90,10 +91,35 @@ public class TunnelConveyor extends Block{
|
|||||||
return new TunnelEntity();
|
return new TunnelEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Array<Object> getDebugInfo(Tile tile){
|
||||||
|
TunnelEntity entity = tile.entity();
|
||||||
|
Array<Object> arr = super.getDebugInfo(tile);
|
||||||
|
for(int i = 0; i < 4; i ++){
|
||||||
|
arr.add("nearby." + i);
|
||||||
|
arr.add(tile.getNearby(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.add("buffer");
|
||||||
|
arr.add(entity.index);
|
||||||
|
|
||||||
|
for(int i = 0; i < entity.index; i++){
|
||||||
|
long l = entity.items[i];
|
||||||
|
float time = NumberUtils.intBitsToFloat(Bits.getLeftInt(l));
|
||||||
|
Item item = Item.getByID(Bits.getRightInt(l));
|
||||||
|
Tile dest = getDestTunnel(tile, item);
|
||||||
|
arr.add(" buffer.item");
|
||||||
|
arr.add(time + " | " + item.name + " | " + ( dest == null ? "no dest" : dest.block() + ":" + dest.floor()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
Tile getDestTunnel(Tile tile, Item item){
|
Tile getDestTunnel(Tile tile, Item item){
|
||||||
Tile dest = tile;
|
Tile dest = tile;
|
||||||
int rel = (tile.getRotation() + 2)%4;
|
int rel = (tile.getRotation() + 2)%4;
|
||||||
for(int i = 0; i < maxdist; i ++){
|
for(int i = 0; i < maxdist; i ++){
|
||||||
|
if(dest == null) return null;
|
||||||
dest = dest.getNearby(rel);
|
dest = dest.getNearby(rel);
|
||||||
if(dest != null && dest.block() instanceof TunnelConveyor && dest.getRotation() == rel
|
if(dest != null && dest.block() instanceof TunnelConveyor && dest.getRotation() == rel
|
||||||
&& dest.getNearby(rel) != null
|
&& dest.getNearby(rel) != null
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public class WebsocketClient implements ClientProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<IOException> failed) {
|
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed) {
|
||||||
if(!Platform.instance.canJoinGame()) {
|
if(!Platform.instance.canJoinGame()) {
|
||||||
failed.accept(new IOException());
|
failed.accept(new IOException());
|
||||||
}else {
|
}else {
|
||||||
@@ -119,7 +119,7 @@ public class WebsocketClient implements ClientProvider {
|
|||||||
public void onMessage(String msg) {
|
public void onMessage(String msg) {
|
||||||
if(!msg.startsWith("---")) return;
|
if(!msg.startsWith("---")) return;
|
||||||
String[] text = msg.substring(3).split("\\|");
|
String[] text = msg.substring(3).split("\\|");
|
||||||
Host host = new Host(text[1], address, Strings.parseInt(text[0]));
|
Host host = new Host(text[1], address, text[2], Strings.parseInt(text[3]), Strings.parseInt(text[0]));
|
||||||
valid.accept(host);
|
valid.accept(host);
|
||||||
accepted[0] = true;
|
accepted[0] = true;
|
||||||
socket.close();
|
socket.close();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import io.anuke.mindustry.net.Net.SendMode;
|
|||||||
import io.anuke.mindustry.net.Packets.Connect;
|
import io.anuke.mindustry.net.Packets.Connect;
|
||||||
import io.anuke.mindustry.net.Packets.Disconnect;
|
import io.anuke.mindustry.net.Packets.Disconnect;
|
||||||
import io.anuke.ucore.function.Consumer;
|
import io.anuke.ucore.function.Consumer;
|
||||||
|
import io.anuke.ucore.util.Strings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.DatagramPacket;
|
import java.net.DatagramPacket;
|
||||||
@@ -34,7 +35,7 @@ public class KryoClient implements ClientProvider{
|
|||||||
handler = new ClientDiscoveryHandler() {
|
handler = new ClientDiscoveryHandler() {
|
||||||
@Override
|
@Override
|
||||||
public DatagramPacket onRequestNewDatagramPacket() {
|
public DatagramPacket onRequestNewDatagramPacket() {
|
||||||
return new DatagramPacket(new byte[32], 32);
|
return new DatagramPacket(new byte[128], 128);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -140,7 +141,7 @@ public class KryoClient implements ClientProvider{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<IOException> invalid){
|
public void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> invalid){
|
||||||
runAsync(() -> {
|
runAsync(() -> {
|
||||||
try {
|
try {
|
||||||
DatagramSocket socket = new DatagramSocket();
|
DatagramSocket socket = new DatagramSocket();
|
||||||
@@ -162,7 +163,7 @@ public class KryoClient implements ClientProvider{
|
|||||||
} else {
|
} else {
|
||||||
Gdx.app.postRunnable(() -> invalid.accept(new IOException("Outdated server.")));
|
Gdx.app.postRunnable(() -> invalid.accept(new IOException("Outdated server.")));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
Gdx.app.postRunnable(() -> invalid.accept(e));
|
Gdx.app.postRunnable(() -> invalid.accept(e));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -209,7 +210,7 @@ public class KryoClient implements ClientProvider{
|
|||||||
if(e instanceof KryoNetException){
|
if(e instanceof KryoNetException){
|
||||||
Gdx.app.postRunnable(() -> Net.showError("$text.server.mismatch"));
|
Gdx.app.postRunnable(() -> Net.showError("$text.server.mismatch"));
|
||||||
}else{
|
}else{
|
||||||
//TODO better exception handling.
|
Net.showError(Strings.parseException(e, true));
|
||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package io.anuke.kryonet;
|
|||||||
|
|
||||||
import com.esotericsoftware.minlog.Log;
|
import com.esotericsoftware.minlog.Log;
|
||||||
import com.esotericsoftware.minlog.Log.Logger;
|
import com.esotericsoftware.minlog.Log.Logger;
|
||||||
import io.anuke.mindustry.Vars;
|
|
||||||
import io.anuke.mindustry.net.Host;
|
import io.anuke.mindustry.net.Host;
|
||||||
import io.anuke.ucore.util.ColorCodes;
|
import io.anuke.ucore.util.ColorCodes;
|
||||||
|
|
||||||
@@ -11,8 +10,7 @@ import java.io.StringWriter;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.headless;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
import static io.anuke.mindustry.Vars.playerGroup;
|
|
||||||
|
|
||||||
public class KryoRegistrator {
|
public class KryoRegistrator {
|
||||||
public static boolean fakeLag = false;
|
public static boolean fakeLag = false;
|
||||||
@@ -49,24 +47,44 @@ public class KryoRegistrator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static ByteBuffer writeServerData(){
|
public static ByteBuffer writeServerData(){
|
||||||
String host = headless ? "Server" : Vars.player.name;
|
int maxlen = 32;
|
||||||
|
|
||||||
|
String host = (headless ? "Server" : player.name);
|
||||||
|
String map = world.getMap().name;
|
||||||
|
|
||||||
|
host = host.substring(0, Math.min(host.length(), maxlen));
|
||||||
|
map = map.substring(0, Math.min(map.length(), maxlen));
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(128);
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(1 + host.getBytes().length + 4);
|
|
||||||
buffer.put((byte)host.getBytes().length);
|
buffer.put((byte)host.getBytes().length);
|
||||||
buffer.put(host.getBytes());
|
buffer.put(host.getBytes());
|
||||||
|
|
||||||
|
buffer.put((byte)map.getBytes().length);
|
||||||
|
buffer.put(map.getBytes());
|
||||||
|
|
||||||
buffer.putInt(playerGroup.size());
|
buffer.putInt(playerGroup.size());
|
||||||
|
buffer.putInt(state.wave);
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Host readServerData(InetAddress ia, ByteBuffer buffer){
|
public static Host readServerData(InetAddress ia, ByteBuffer buffer){
|
||||||
//old version address.
|
if(buffer.capacity() < 128) return null; //old version address.
|
||||||
if(buffer.capacity() == 4) return null;
|
|
||||||
|
byte hlength = buffer.get();
|
||||||
|
byte[] hb = new byte[hlength];
|
||||||
|
buffer.get(hb);
|
||||||
|
|
||||||
|
byte mlength = buffer.get();
|
||||||
|
byte[] mb = new byte[mlength];
|
||||||
|
buffer.get(mb);
|
||||||
|
|
||||||
|
String host = new String(hb);
|
||||||
|
String map = new String(mb);
|
||||||
|
|
||||||
byte length = buffer.get();
|
|
||||||
byte[] sname = new byte[length];
|
|
||||||
buffer.get(sname);
|
|
||||||
int players = buffer.getInt();
|
int players = buffer.getInt();
|
||||||
|
int wave = buffer.getInt();
|
||||||
|
|
||||||
return new Host(new String(sname), ia.getHostAddress(), players);
|
return new Host(host, ia.getHostAddress(), map, wave, players);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,7 @@ import io.anuke.mindustry.net.Net;
|
|||||||
import io.anuke.mindustry.net.Net.SendMode;
|
import io.anuke.mindustry.net.Net.SendMode;
|
||||||
import io.anuke.mindustry.net.Net.ServerProvider;
|
import io.anuke.mindustry.net.Net.ServerProvider;
|
||||||
import io.anuke.mindustry.net.NetConnection;
|
import io.anuke.mindustry.net.NetConnection;
|
||||||
import io.anuke.mindustry.net.Packets.Connect;
|
import io.anuke.mindustry.net.Packets.*;
|
||||||
import io.anuke.mindustry.net.Packets.Disconnect;
|
|
||||||
import io.anuke.mindustry.net.Packets.KickPacket;
|
|
||||||
import io.anuke.mindustry.net.Packets.KickReason;
|
|
||||||
import io.anuke.mindustry.net.Registrator;
|
import io.anuke.mindustry.net.Registrator;
|
||||||
import io.anuke.mindustry.net.Streamable;
|
import io.anuke.mindustry.net.Streamable;
|
||||||
import io.anuke.mindustry.net.Streamable.StreamBegin;
|
import io.anuke.mindustry.net.Streamable.StreamBegin;
|
||||||
@@ -25,6 +22,7 @@ import io.anuke.mindustry.net.Streamable.StreamChunk;
|
|||||||
import io.anuke.ucore.UCore;
|
import io.anuke.ucore.UCore;
|
||||||
import io.anuke.ucore.core.Timers;
|
import io.anuke.ucore.core.Timers;
|
||||||
import io.anuke.ucore.util.Log;
|
import io.anuke.ucore.util.Log;
|
||||||
|
import io.anuke.ucore.util.Strings;
|
||||||
import org.java_websocket.WebSocket;
|
import org.java_websocket.WebSocket;
|
||||||
import org.java_websocket.exceptions.WebsocketNotConnectedException;
|
import org.java_websocket.exceptions.WebsocketNotConnectedException;
|
||||||
import org.java_websocket.handshake.ClientHandshake;
|
import org.java_websocket.handshake.ClientHandshake;
|
||||||
@@ -66,13 +64,15 @@ public class KryoServer implements ServerProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connected (Connection connection) {
|
public void connected (Connection connection) {
|
||||||
KryoConnection kn = new KryoConnection(lastconnection ++, connection.getRemoteAddressTCP().toString(), connection);
|
String ip = connection.getRemoteAddressTCP().getAddress().getHostAddress();
|
||||||
|
|
||||||
|
KryoConnection kn = new KryoConnection(lastconnection ++, ip, connection);
|
||||||
|
|
||||||
Connect c = new Connect();
|
Connect c = new Connect();
|
||||||
c.id = kn.id;
|
c.id = kn.id;
|
||||||
c.addressTCP = connection.getRemoteAddressTCP().toString();
|
c.addressTCP = ip;
|
||||||
|
|
||||||
Log.info("&bRecieved connection: {0} {1}", c.id, c.addressTCP);
|
Log.info("&bRecieved connection: {0} / {1}", c.id, c.addressTCP);
|
||||||
|
|
||||||
connections.add(kn);
|
connections.add(kn);
|
||||||
Gdx.app.postRunnable(() -> Net.handleServerReceived(kn.id, c));
|
Gdx.app.postRunnable(() -> Net.handleServerReceived(kn.id, c));
|
||||||
@@ -141,12 +141,15 @@ public class KryoServer implements ServerProvider {
|
|||||||
if(con == null){
|
if(con == null){
|
||||||
Log.err("Cannot kick unknown player!");
|
Log.err("Cannot kick unknown player!");
|
||||||
return;
|
return;
|
||||||
|
}else{
|
||||||
|
Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
KickPacket p = new KickPacket();
|
KickPacket p = new KickPacket();
|
||||||
p.reason = reason;
|
p.reason = reason;
|
||||||
|
|
||||||
con.send(p, SendMode.tcp);
|
con.send(p, SendMode.tcp);
|
||||||
|
Timers.runTask(2f, con::close);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -371,9 +374,21 @@ public class KryoServer implements ServerProvider {
|
|||||||
connection.sendUDP(object);
|
connection.sendUDP(object);
|
||||||
}
|
}
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
e.printStackTrace();
|
Log.err(e);
|
||||||
Log.info("Disconnecting invalid client!");
|
Log.info("Disconnecting invalid client!");
|
||||||
|
try{
|
||||||
|
NetErrorPacket packet = new NetErrorPacket();
|
||||||
|
packet.message = Strings.parseException(e, true);
|
||||||
|
Timers.runTask(5f, connection::close);
|
||||||
|
}catch (Exception e2){
|
||||||
|
Log.err(e2);
|
||||||
|
connection.close();
|
||||||
|
}
|
||||||
connection.close();
|
connection.close();
|
||||||
|
|
||||||
|
KryoConnection k = getByKryoID(connection.getID());
|
||||||
|
if(k != null) connections.remove(k);
|
||||||
|
Log.info("Connection removed {0}", k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -398,7 +413,7 @@ public class KryoServer implements ServerProvider {
|
|||||||
@Override
|
@Override
|
||||||
public void onOpen(WebSocket conn, ClientHandshake handshake) {
|
public void onOpen(WebSocket conn, ClientHandshake handshake) {
|
||||||
Connect connect = new Connect();
|
Connect connect = new Connect();
|
||||||
connect.addressTCP = conn.getRemoteSocketAddress().toString();
|
connect.addressTCP = conn.getRemoteSocketAddress().getAddress().getHostAddress();
|
||||||
KryoConnection kn = new KryoConnection(lastconnection ++, connect.addressTCP, conn);
|
KryoConnection kn = new KryoConnection(lastconnection ++, connect.addressTCP, conn);
|
||||||
|
|
||||||
Log.info("&bRecieved web connection: {0} {1}", kn.id, connect.addressTCP);
|
Log.info("&bRecieved web connection: {0} {1}", kn.id, connect.addressTCP);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import io.anuke.mindustry.net.Net;
|
|||||||
import io.anuke.mindustry.net.NetEvents;
|
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.Map;
|
import io.anuke.mindustry.world.Map;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
@@ -38,7 +39,12 @@ public class ServerControl extends Module {
|
|||||||
private ShuffleMode mode;
|
private ShuffleMode mode;
|
||||||
|
|
||||||
public ServerControl(){
|
public ServerControl(){
|
||||||
Settings.defaults("shufflemode", "normal");
|
Settings.defaultList(
|
||||||
|
"shufflemode", "normal",
|
||||||
|
"bans", "",
|
||||||
|
"admins", ""
|
||||||
|
);
|
||||||
|
|
||||||
mode = ShuffleMode.valueOf(Settings.getString("shufflemode"));
|
mode = ShuffleMode.valueOf(Settings.getString("shufflemode"));
|
||||||
|
|
||||||
Effects.setScreenShakeProvider((a, b) -> {});
|
Effects.setScreenShakeProvider((a, b) -> {});
|
||||||
@@ -99,7 +105,7 @@ public class ServerControl extends Module {
|
|||||||
handler.register("help", "Displays this command list.", arg -> {
|
handler.register("help", "Displays this command list.", arg -> {
|
||||||
info("Commands:");
|
info("Commands:");
|
||||||
for(Command command : handler.getCommandList()){
|
for(Command command : handler.getCommandList()){
|
||||||
print(" &y" + command.text + (command.params.isEmpty() ? "" : " ") + command.params + " - &lm" + command.description);
|
print(" &y" + command.text + (command.paramText.isEmpty() ? "" : " ") + command.paramText + " - &lm" + command.description);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -184,7 +190,22 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("say", "<message>", "Send a message to all players.", arg -> {
|
handler.register("players", "Display player info.", arg -> {
|
||||||
|
if(state.is(State.menu)){
|
||||||
|
info("&lyServer is closed.");
|
||||||
|
}else{
|
||||||
|
if(playerGroup.size() > 0) {
|
||||||
|
info("&lyPlayers: {0}", playerGroup.size());
|
||||||
|
for (Player p : playerGroup.all()) {
|
||||||
|
print(" &y{0} / Connection {1} / IP: {2}", p.name, p.clientid, Net.getConnection(p.clientid).address);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
info("&lyNo players connected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("say", "<message...>", "Send a message to all players.", arg -> {
|
||||||
if(!state.is(State.playing)) {
|
if(!state.is(State.playing)) {
|
||||||
err("Not hosting. Host a game first.");
|
err("Not hosting. Host a game first.");
|
||||||
return;
|
return;
|
||||||
@@ -192,7 +213,7 @@ public class ServerControl extends Module {
|
|||||||
|
|
||||||
netCommon.sendMessage("[GRAY][[Server]:[] " + arg[0]);
|
netCommon.sendMessage("[GRAY][[Server]:[] " + arg[0]);
|
||||||
info("&lyServer: &lb{0}", arg[0]);
|
info("&lyServer: &lb{0}", arg[0]);
|
||||||
}).mergeArgs();
|
});
|
||||||
|
|
||||||
handler.register("difficulty", "<difficulty>", "Set game difficulty.", arg -> {
|
handler.register("difficulty", "<difficulty>", "Set game difficulty.", arg -> {
|
||||||
try{
|
try{
|
||||||
@@ -237,11 +258,6 @@ public class ServerControl extends Module {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(playerGroup.size() == 0){
|
|
||||||
err("But this server is empty. A barren wasteland.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player target = null;
|
Player target = null;
|
||||||
|
|
||||||
for(Player player : playerGroup.all()){
|
for(Player player : playerGroup.all()){
|
||||||
@@ -259,6 +275,130 @@ public class ServerControl extends Module {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
handler.register("ban", "<username>", "Ban a person by name.", arg -> {
|
||||||
|
if(!state.is(State.playing)) {
|
||||||
|
err("Can't ban people by name with no players.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player target = null;
|
||||||
|
|
||||||
|
for(Player player : playerGroup.all()){
|
||||||
|
if(player.name.equalsIgnoreCase(arg[0])){
|
||||||
|
target = player;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target != null){
|
||||||
|
String ip = Net.getConnection(player.clientid).address;
|
||||||
|
netServer.admins.banPlayer(ip);
|
||||||
|
Net.kickConnection(target.clientid, KickReason.banned);
|
||||||
|
info("Banned player by IP: {0}", ip);
|
||||||
|
}else{
|
||||||
|
info("Nobody with that name could be found.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("bans", "List all banned IPs.", arg -> {
|
||||||
|
Array<String> bans = netServer.admins.getBanned();
|
||||||
|
|
||||||
|
if(bans.size == 0){
|
||||||
|
Log.info("No banned players have been found.");
|
||||||
|
}else{
|
||||||
|
Log.info("&lyBanned players:");
|
||||||
|
for(String string : bans){
|
||||||
|
Log.info(" &luy {0} / Last known name: '{1}'", string, netServer.admins.getLastName(string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("banip", "<ip>", "Ban a person by IP.", arg -> {
|
||||||
|
if(netServer.admins.banPlayer(arg[0])) {
|
||||||
|
info("Banned player by IP: {0}.", arg[0]);
|
||||||
|
|
||||||
|
for(Player player : playerGroup.all()){
|
||||||
|
if(Net.getConnection(player.clientid).address.equals(arg[0])){
|
||||||
|
Net.kickConnection(player.clientid, KickReason.banned);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
err("That IP is already banned!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("unbanip", "<ip>", "Unban a person by IP.", arg -> {
|
||||||
|
if(netServer.admins.unbanPlayer(arg[0])) {
|
||||||
|
info("Unbanned player by IP: {0}.", arg[0]);
|
||||||
|
}else{
|
||||||
|
err("That IP is not banned!");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("admin", "<username>", "Make a user admin", arg -> {
|
||||||
|
if(!state.is(State.playing)) {
|
||||||
|
err("Open the server first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player target = null;
|
||||||
|
|
||||||
|
for(Player player : playerGroup.all()){
|
||||||
|
if(player.name.equalsIgnoreCase(arg[0])){
|
||||||
|
target = player;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target != null){
|
||||||
|
String ip = Net.getConnection(player.clientid).address;
|
||||||
|
netServer.admins.adminPlayer(ip);
|
||||||
|
NetEvents.handleAdminSet(target, true);
|
||||||
|
info("Admin-ed player by IP: {0} / {1}", ip, arg[0]);
|
||||||
|
}else{
|
||||||
|
info("Nobody with that name could be found.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("unadmin", "<username>", "Removes admin status from a player", arg -> {
|
||||||
|
if(!state.is(State.playing)) {
|
||||||
|
err("Open the server first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player target = null;
|
||||||
|
|
||||||
|
for(Player player : playerGroup.all()){
|
||||||
|
if(player.name.equalsIgnoreCase(arg[0])){
|
||||||
|
target = player;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target != null){
|
||||||
|
String ip = Net.getConnection(player.clientid).address;
|
||||||
|
netServer.admins.unAdminPlayer(ip);
|
||||||
|
NetEvents.handleAdminSet(target, false);
|
||||||
|
info("Un-admin-ed player by IP: {0} / {1}", ip, arg[0]);
|
||||||
|
}else{
|
||||||
|
info("Nobody with that name could be found.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
handler.register("admins", "List all banned IPs.", arg -> {
|
||||||
|
Array<String> admins = netServer.admins.getAdmins();
|
||||||
|
|
||||||
|
if(admins.size == 0){
|
||||||
|
Log.info("No admins have been found.");
|
||||||
|
}else{
|
||||||
|
Log.info("&lyAdmins:");
|
||||||
|
for(String string : admins){
|
||||||
|
Log.info(" &luy {0} / Name: '{1}'", string, netServer.admins.getLastName(string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
handler.register("runwave", "Trigger the next wave.", arg -> {
|
handler.register("runwave", "Trigger the next wave.", arg -> {
|
||||||
if(!state.is(State.playing)) {
|
if(!state.is(State.playing)) {
|
||||||
err("Not hosting. Host a game first.");
|
err("Not hosting. Host a game first.");
|
||||||
@@ -317,7 +457,7 @@ public class ServerControl extends Module {
|
|||||||
info(DebugFragment.debugInfo());
|
info(DebugFragment.debugInfo());
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.register("trace", "<x> <y>", "Prints debug info about a block", arg -> {
|
handler.register("traceblock", "<x> <y>", "Prints debug info about a block", arg -> {
|
||||||
try{
|
try{
|
||||||
int x = Integer.parseInt(arg[0]);
|
int x = Integer.parseInt(arg[0]);
|
||||||
int y = Integer.parseInt(arg[1]);
|
int y = Integer.parseInt(arg[1]);
|
||||||
@@ -343,6 +483,39 @@ public class ServerControl extends Module {
|
|||||||
Log.err("Invalid coordinates passed.");
|
Log.err("Invalid coordinates passed.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
handler.register("trace", "<username>", "Trace a player's actions", arg -> {
|
||||||
|
if(!state.is(State.playing)) {
|
||||||
|
err("Open the server first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player target = null;
|
||||||
|
|
||||||
|
for(Player player : playerGroup.all()){
|
||||||
|
if(player.name.equalsIgnoreCase(arg[0])){
|
||||||
|
target = player;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target != null){
|
||||||
|
TraceInfo info = netServer.admins.getTrace(Net.getConnection(target.clientid).address);
|
||||||
|
Log.info("&lcTrace info for player '{0}':", target.name);
|
||||||
|
Log.info(" &lyEntity ID: {0}", info. playerid);
|
||||||
|
Log.info(" &lyIP: {0}", info.ip);
|
||||||
|
Log.info(" &lycustom client: {0}", info.modclient);
|
||||||
|
Log.info("");
|
||||||
|
Log.info(" &lytotal blocks broken: {0}", info.totalBlocksBroken);
|
||||||
|
Log.info(" &lystructure blocks broken: {0}", info.structureBlocksBroken);
|
||||||
|
Log.info(" &lylast block broken: {0}", info.lastBlockBroken.formalName);
|
||||||
|
Log.info("");
|
||||||
|
Log.info(" &lytotal blocks placed: {0}", info.totalBlocksPlaced);
|
||||||
|
Log.info(" &lylast block placed: {0}", info.lastBlockPlaced.formalName);
|
||||||
|
}else{
|
||||||
|
info("Nobody with that name could be found.");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readCommands(){
|
private void readCommands(){
|
||||||
@@ -355,8 +528,10 @@ public class ServerControl extends Module {
|
|||||||
|
|
||||||
if (response.type == ResponseType.unknownCommand) {
|
if (response.type == ResponseType.unknownCommand) {
|
||||||
err("Invalid command. Type 'help' for help.");
|
err("Invalid command. Type 'help' for help.");
|
||||||
} else if (response.type == ResponseType.invalidArguments) {
|
}else if (response.type == ResponseType.fewArguments) {
|
||||||
err("Invalid command arguments. Usage: " + response.command.text + " " + response.command.params);
|
err("Too few command arguments. Usage: " + response.command.text + " " + response.command.paramText);
|
||||||
|
}else if (response.type == ResponseType.manyArguments) {
|
||||||
|
err("Too many command arguments. Usage: " + response.command.text + " " + response.command.paramText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user