diff --git a/.gitignore b/.gitignore index eef26a8b5e..c47b756d8a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ *.war *.ear hs_err_pid* +crash-report-* ## Robovm /ios/robovm-build/ diff --git a/android/src/io/anuke/mindustry/AndroidLauncher.java b/android/src/io/anuke/mindustry/AndroidLauncher.java index 872db50400..6c432a933a 100644 --- a/android/src/io/anuke/mindustry/AndroidLauncher.java +++ b/android/src/io/anuke/mindustry/AndroidLauncher.java @@ -1,24 +1,25 @@ package io.anuke.mindustry; -import java.text.DateFormat; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; -import java.util.Date; - -import com.badlogic.gdx.backends.android.AndroidApplication; -import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; - -import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.telephony.TelephonyManager; +import com.badlogic.gdx.backends.android.AndroidApplication; +import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; +import io.anuke.kryonet.KryoClient; +import io.anuke.kryonet.KryoServer; import io.anuke.mindustry.io.PlatformFunction; +import io.anuke.mindustry.net.Net; import io.anuke.ucore.function.Callable; import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.layout.Unit; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + public class AndroidLauncher extends AndroidApplication{ boolean doubleScaleTablets = true; @@ -72,6 +73,9 @@ public class AndroidLauncher extends AndroidApplication{ } config.hideStatusBar = true; + + Net.setClientProvider(new KryoClient()); + Net.setServerProvider(new KryoServer()); initialize(new Mindustry(), config); } diff --git a/build.gradle b/build.gradle index f3567a64ba..803377a7f7 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ allprojects { appName = "Mindustry" gdxVersion = '1.9.8' aiVersion = '1.8.1' - uCoreVersion = '4fb2c63'; + uCoreVersion = '8751c0a66c'; } repositories { @@ -38,10 +38,10 @@ project(":desktop") { dependencies { compile project(":core") + compile project(":kryonet") compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion" - compile "com.esotericsoftware:kryonet:2.22.0-RC1" compile "com.github.MinnDevelopment:Java-DiscordRPC:dbd4aac" } } @@ -71,6 +71,7 @@ project(":android") { dependencies { compile project(":core") + compile project(":kryonet") compile "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi" natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a" @@ -79,7 +80,6 @@ project(":android") { natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64" compile "com.badlogicgames.gdx:gdx-ai:$aiVersion" compile "com.badlogicgames.gdx:gdx-controllers-android:$gdxVersion" - compile "com.esotericsoftware:kryonet:2.22.0-RC1" } } @@ -110,6 +110,15 @@ project(":core") { } } +project(":kryonet") { + apply plugin: "java" + + dependencies { + compile project(":core") + compile 'com.github.crykn:kryonet:2.22.1' + } +} + tasks.eclipse.doLast { delete ".project" } diff --git a/core/assets-raw/sprites/mechs/ship-standard.png b/core/assets-raw/sprites/mechs/ship-standard.png new file mode 100644 index 0000000000..d0d0dd52ec Binary files /dev/null and b/core/assets-raw/sprites/mechs/ship-standard.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-host.png b/core/assets-raw/sprites/ui/icons/icon-host.png new file mode 100644 index 0000000000..82d5d2d153 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-host.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 009fc28948..311df85b8d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -12,7 +12,13 @@ text.savegame=Save Game text.loadgame=Load Game text.joingame=Join Game text.quit=Quit +text.server.connected=A player has joined. +text.server.disconnected={0} has disconnected. text.hostserver=Host Server +text.host=Host +text.hosts.discovering=Discovering LAN games +text.hosts.none=[lightgray]No hosts found! +text.joingame.byip=Join by IP... text.joingame.title=Join Game text.joingame.ip=IP: text.disconnect=Disconnected. diff --git a/core/assets/sprites/sprites.atlas b/core/assets/sprites/sprites.atlas index 8828cba6e3..8d8c150fcc 100644 --- a/core/assets/sprites/sprites.atlas +++ b/core/assets/sprites/sprites.atlas @@ -27,49 +27,49 @@ blocks/blackrock1 index: -1 blocks/blackrockshadow1 rotate: false - xy: 195, 75 + xy: 482, 95 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstone1 rotate: false - xy: 205, 75 + xy: 988, 485 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstone2 rotate: false - xy: 215, 75 + xy: 998, 485 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstone3 rotate: false - xy: 225, 75 + xy: 1008, 485 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstoneblock1 rotate: false - xy: 235, 75 + xy: 841, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstoneblock2 rotate: false - xy: 245, 75 + xy: 841, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/blackstoneblock3 rotate: false - xy: 255, 75 + xy: 851, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -83,7 +83,7 @@ blocks/blackstoneedge index: -1 blocks/block rotate: false - xy: 275, 75 + xy: 861, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -111,119 +111,119 @@ blocks/chainturret index: -1 blocks/chainturret-icon rotate: false - xy: 109, 65 + xy: 109, 35 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/coal1 rotate: false - xy: 295, 75 + xy: 871, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coal2 rotate: false - xy: 531, 136 + xy: 871, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coal3 rotate: false - xy: 527, 126 + xy: 881, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coaldrill rotate: false - xy: 524, 107 + xy: 881, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coalgenerator rotate: false - xy: 305, 79 + xy: 891, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coalgenerator-top rotate: false - xy: 315, 79 + xy: 891, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/coalpurifier rotate: false - xy: 325, 79 + xy: 901, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/combustiongenerator rotate: false - xy: 335, 79 + xy: 901, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/compositewall rotate: false - xy: 345, 79 + xy: 911, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduit rotate: false - xy: 355, 79 + xy: 911, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduitbottom rotate: false - xy: 365, 83 + xy: 921, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduitliquid rotate: false - xy: 375, 83 + xy: 921, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conduittop rotate: false - xy: 385, 83 + xy: 931, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conveyor rotate: false - xy: 395, 83 + xy: 931, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conveyormove rotate: false - xy: 405, 83 + xy: 941, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/conveyortunnel rotate: false - xy: 415, 83 + xy: 941, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -237,42 +237,42 @@ blocks/core index: -1 blocks/cross rotate: false - xy: 425, 83 + xy: 951, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/crucible rotate: false - xy: 435, 83 + xy: 951, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/deepwater rotate: false - xy: 445, 83 + xy: 961, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/dirt1 rotate: false - xy: 455, 83 + xy: 961, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/dirt2 rotate: false - xy: 465, 83 + xy: 971, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/dirt3 rotate: false - xy: 475, 83 + xy: 971, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -286,7 +286,7 @@ blocks/dirtedge index: -1 blocks/door rotate: false - xy: 786, 393 + xy: 851, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -300,7 +300,7 @@ blocks/door-large index: -1 blocks/door-large-icon rotate: false - xy: 796, 393 + xy: 861, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -314,21 +314,21 @@ blocks/door-large-open index: -1 blocks/door-open rotate: false - xy: 844, 485 + xy: 871, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/doubleturret rotate: false - xy: 109, 53 + xy: 95, 7 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/duriumwall rotate: false - xy: 854, 485 + xy: 881, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -342,266 +342,266 @@ blocks/duriumwall-large index: -1 blocks/duriumwall-large-icon rotate: false - xy: 864, 485 + xy: 891, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/enemyspawn rotate: false - xy: 874, 485 + xy: 901, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/flameturret rotate: false - xy: 135, 79 + xy: 536, 181 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/fluxpump rotate: false - xy: 894, 485 + xy: 921, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grass1 rotate: false - xy: 904, 485 + xy: 931, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grass2 rotate: false - xy: 914, 485 + xy: 941, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grass3 rotate: false - xy: 924, 485 + xy: 951, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grassblock1 rotate: false - xy: 934, 485 + xy: 961, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grassblock2 rotate: false - xy: 944, 485 + xy: 971, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/grassedge rotate: false - xy: 136, 91 + xy: 152, 91 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/ice1 rotate: false - xy: 954, 485 + xy: 981, 473 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/ice2 rotate: false - xy: 964, 485 + xy: 981, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/ice3 rotate: false - xy: 974, 485 + xy: 981, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iceedge rotate: false - xy: 150, 91 + xy: 166, 91 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/icerock1 rotate: false - xy: 984, 485 + xy: 991, 475 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/icerock2 rotate: false - xy: 994, 485 + xy: 991, 465 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/icerockshadow1 rotate: false - xy: 1004, 485 + xy: 1001, 475 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rockshadow1 rotate: false - xy: 1004, 485 + xy: 1001, 475 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/icerockshadow2 rotate: false - xy: 1014, 485 + xy: 991, 455 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rockshadow2 rotate: false - xy: 1014, 485 + xy: 991, 455 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iron1 rotate: false - xy: 861, 465 + xy: 512, 82 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iron2 rotate: false - xy: 871, 475 + xy: 522, 82 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/iron3 rotate: false - xy: 861, 455 + xy: 801, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/irondrill rotate: false - xy: 871, 465 + xy: 811, 445 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/ironwall rotate: false - xy: 881, 475 + xy: 821, 445 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/junction rotate: false - xy: 871, 455 + xy: 831, 445 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/laserturret rotate: false - xy: 351, 89 + xy: 844, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/lava rotate: false - xy: 881, 465 + xy: 841, 444 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/lavaedge rotate: false - xy: 164, 91 + xy: 95, 75 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/lavasmelter rotate: false - xy: 891, 475 + xy: 851, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/liquiditemjunction rotate: false - xy: 881, 455 + xy: 861, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/liquidjunction rotate: false - xy: 891, 465 + xy: 871, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/liquidrouter rotate: false - xy: 901, 475 + xy: 881, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/machineturret rotate: false - xy: 363, 93 + xy: 856, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/megarepairturret rotate: false - xy: 375, 93 + xy: 868, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/mortarturret rotate: false - xy: 387, 93 + xy: 880, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/mossblock rotate: false - xy: 901, 465 + xy: 901, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/mossstone rotate: false - xy: 901, 465 + xy: 901, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -622,7 +622,7 @@ blocks/nuclearreactor-center index: -1 blocks/nuclearreactor-icon rotate: false - xy: 901, 455 + xy: 921, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -643,196 +643,196 @@ blocks/nuclearreactor-small index: -1 blocks/oil rotate: false - xy: 911, 465 + xy: 931, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/oiledge rotate: false - xy: 95, 61 + xy: 109, 75 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/oilrefinery rotate: false - xy: 921, 475 + xy: 941, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/omnidrill rotate: false - xy: 911, 455 + xy: 951, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/plasmaturret rotate: false - xy: 399, 93 + xy: 892, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/playerspawn rotate: false - xy: 921, 465 + xy: 961, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerbooster rotate: false - xy: 931, 475 + xy: 971, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/poweredconveyor rotate: false - xy: 921, 455 + xy: 981, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/poweredconveyormove rotate: false - xy: 931, 465 + xy: 991, 445 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerlaser rotate: false - xy: 941, 475 + xy: 1001, 445 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerlasercorner rotate: false - xy: 931, 455 + xy: 1011, 445 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/powerlaserrouter rotate: false - xy: 941, 465 + xy: 109, 13 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pulseconduit rotate: false - xy: 951, 475 + xy: 736, 381 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pulseconduitbottom rotate: false - xy: 941, 455 + xy: 740, 371 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pulseconduittop rotate: false - xy: 951, 465 + xy: 740, 361 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/pump rotate: false - xy: 961, 475 + xy: 740, 351 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/repairturret rotate: false - xy: 411, 93 + xy: 904, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/rock1 rotate: false - xy: 961, 465 + xy: 740, 331 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rock2 rotate: false - xy: 971, 475 + xy: 740, 321 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/router rotate: false - xy: 961, 455 + xy: 798, 393 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rtgenerator rotate: false - xy: 971, 465 + xy: 180, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/rtgenerator-top rotate: false - xy: 981, 475 + xy: 190, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sand1 rotate: false - xy: 971, 455 + xy: 200, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sand2 rotate: false - xy: 981, 465 + xy: 210, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sand3 rotate: false - xy: 991, 475 + xy: 220, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sandblock1 rotate: false - xy: 981, 455 + xy: 230, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sandblock2 rotate: false - xy: 991, 465 + xy: 240, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sandblock3 rotate: false - xy: 1001, 475 + xy: 250, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -846,91 +846,91 @@ blocks/sandedge index: -1 blocks/shadow rotate: false - xy: 423, 93 + xy: 916, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/shieldgenerator rotate: false - xy: 1001, 465 + xy: 270, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/shotgunturret rotate: false - xy: 435, 93 + xy: 928, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/shrub rotate: false - xy: 1011, 465 + xy: 527, 126 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/shrubshadow rotate: false - xy: 1011, 455 + xy: 527, 116 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/smelter rotate: false - xy: 315, 69 + xy: 524, 106 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/sniperturret rotate: false - xy: 447, 93 + xy: 940, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/snow1 rotate: false - xy: 325, 69 + xy: 362, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snow2 rotate: false - xy: 335, 69 + xy: 372, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snow3 rotate: false - xy: 345, 69 + xy: 382, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snowblock1 rotate: false - xy: 355, 69 + xy: 392, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snowblock2 rotate: false - xy: 365, 73 + xy: 402, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/snowblock3 rotate: false - xy: 375, 73 + xy: 412, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -944,28 +944,28 @@ blocks/snowedge index: -1 blocks/sorter rotate: false - xy: 385, 73 + xy: 422, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/steelconveyor rotate: false - xy: 395, 73 + xy: 432, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/steelconveyormove rotate: false - xy: 405, 73 + xy: 442, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/steelwall rotate: false - xy: 415, 73 + xy: 452, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -979,98 +979,98 @@ blocks/steelwall-large index: -1 blocks/steelwall-large-icon rotate: false - xy: 425, 73 + xy: 462, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stone1 rotate: false - xy: 435, 73 + xy: 472, 83 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stone2 rotate: false - xy: 445, 73 + xy: 482, 76 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stone3 rotate: false - xy: 455, 73 + xy: 492, 72 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneblock1 rotate: false - xy: 465, 73 + xy: 502, 72 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneblock2 rotate: false - xy: 475, 73 + xy: 512, 72 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneblock3 rotate: false - xy: 533, 178 + xy: 522, 72 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stonedrill rotate: false - xy: 537, 168 + xy: 107, 3 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stoneedge rotate: false - xy: 95, 19 + xy: 109, 47 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/stoneformer rotate: false - xy: 537, 158 + xy: 746, 381 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/stonewall rotate: false - xy: 537, 148 + xy: 750, 371 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/teleporter rotate: false - xy: 801, 443 + xy: 750, 361 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/teleporter-top rotate: false - xy: 811, 445 + xy: 750, 351 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/thermalgenerator rotate: false - xy: 821, 445 + xy: 750, 341 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -1084,56 +1084,56 @@ blocks/titancannon index: -1 blocks/titancannon-icon rotate: false - xy: 459, 93 + xy: 952, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/titanium1 rotate: false - xy: 831, 445 + xy: 750, 331 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titanium2 rotate: false - xy: 841, 445 + xy: 750, 321 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titanium3 rotate: false - xy: 851, 445 + xy: 740, 311 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumdrill rotate: false - xy: 861, 445 + xy: 750, 311 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumpurifier rotate: false - xy: 871, 445 + xy: 734, 301 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumshieldwall rotate: false - xy: 881, 445 + xy: 744, 301 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/titaniumwall rotate: false - xy: 891, 445 + xy: 754, 301 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -1147,77 +1147,77 @@ blocks/titaniumwall-large index: -1 blocks/titaniumwall-large-icon rotate: false - xy: 901, 445 + xy: 528, 96 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/turret rotate: false - xy: 471, 93 + xy: 964, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 blocks/uranium1 rotate: false - xy: 931, 445 + xy: 532, 86 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/uranium2 rotate: false - xy: 941, 445 + xy: 532, 76 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/uranium3 rotate: false - xy: 951, 445 + xy: 542, 86 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/uraniumdrill rotate: false - xy: 961, 445 + xy: 542, 76 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/water rotate: false - xy: 971, 445 + xy: 756, 381 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 blocks/wateredge rotate: false - xy: 109, 77 + xy: 95, 19 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 blocks/waveturret rotate: false - xy: 183, 73 + xy: 976, 483 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 bullet rotate: false - xy: 285, 75 + xy: 861, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 chainbullet rotate: false - xy: 527, 117 + xy: 841, 454 size: 8, 7 orig: 8, 7 offset: 0, 0 @@ -1294,14 +1294,14 @@ enemies/targetenemy-t1 index: -1 enemies/enemy-t2 rotate: false - xy: 108, 91 + xy: 124, 91 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 enemies/enemy-t3 rotate: false - xy: 122, 91 + xy: 138, 91 size: 12, 12 orig: 12, 12 offset: 0, 0 @@ -1392,63 +1392,63 @@ enemies/healerenemy-t3 index: -1 enemies/mortarenemy-t1 rotate: false - xy: 738, 412 + xy: 738, 396 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/mortarenemy-t2 rotate: false - xy: 738, 396 + xy: 754, 414 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/mortarenemy-t3 rotate: false - xy: 754, 414 + xy: 754, 398 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/rapidenemy-t1 rotate: false - xy: 754, 398 + xy: 770, 414 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/rapidenemy-t2 rotate: false - xy: 770, 414 + xy: 770, 398 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/rapidenemy-t3 rotate: false - xy: 770, 398 + xy: 786, 419 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/tankenemy-t1 rotate: false - xy: 786, 419 + xy: 786, 403 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/tankenemy-t2 rotate: false - xy: 786, 403 + xy: 92, 89 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 enemies/tankenemy-t3 rotate: false - xy: 92, 89 + xy: 108, 89 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -1476,63 +1476,63 @@ enemies/titanenemy-t3 index: -1 enemyarrow rotate: false - xy: 305, 70 + xy: 482, 86 size: 8, 7 orig: 8, 7 offset: 0, 0 index: -1 icon-coal rotate: false - xy: 841, 475 + xy: 1001, 465 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-dirium rotate: false - xy: 841, 465 + xy: 1001, 455 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-iron rotate: false - xy: 851, 475 + xy: 1011, 475 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-steel rotate: false - xy: 841, 455 + xy: 1011, 465 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-stone rotate: false - xy: 851, 465 + xy: 1011, 455 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-titanium rotate: false - xy: 861, 475 + xy: 492, 82 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 icon-uranium rotate: false - xy: 851, 455 + xy: 502, 82 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 laser rotate: false - xy: 178, 91 + xy: 533, 178 size: 1, 12 orig: 1, 12 offset: 0, 0 @@ -1553,35 +1553,42 @@ laserfull index: -1 mechs/mech-standard rotate: false - xy: 95, 75 + xy: 95, 61 + size: 12, 12 + orig: 12, 12 + offset: 0, 0 + index: -1 +mechs/ship-standard + rotate: false + xy: 109, 61 size: 12, 12 orig: 12, 12 offset: 0, 0 index: -1 shell rotate: false - xy: 991, 455 + xy: 260, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 shot rotate: false - xy: 1011, 475 + xy: 280, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 shot-long rotate: false - xy: 1001, 455 + xy: 290, 87 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 titanshell rotate: false - xy: 911, 445 + xy: 534, 106 size: 8, 8 orig: 8, 8 offset: 0, 0 @@ -1680,14 +1687,14 @@ ui/check-over index: -1 ui/clear rotate: false - xy: 123, 79 + xy: 109, 23 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/cursor rotate: false - xy: 69, 3 + xy: 734, 311 size: 4, 4 orig: 4, 4 offset: 0, 0 @@ -1708,7 +1715,7 @@ ui/icons/icon-add index: -1 ui/icons/icon-areaDelete rotate: false - xy: 109, 41 + xy: 537, 169 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1722,28 +1729,28 @@ ui/icons/icon-arrow index: -1 ui/icons/icon-arrow-down rotate: false - xy: 147, 79 + xy: 537, 157 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-arrow-left rotate: false - xy: 109, 29 + xy: 537, 145 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-arrow-right rotate: false - xy: 159, 79 + xy: 786, 391 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-arrow-up rotate: false - xy: 95, 7 + xy: 302, 89 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1792,21 +1799,21 @@ ui/icons/icon-close-over index: -1 ui/icons/icon-crafting rotate: false - xy: 109, 17 + xy: 314, 89 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-cursor rotate: false - xy: 107, 5 + xy: 326, 89 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-defense rotate: false - xy: 171, 79 + xy: 338, 89 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1820,7 +1827,7 @@ ui/icons/icon-discord index: -1 ui/icons/icon-distribution rotate: false - xy: 183, 85 + xy: 350, 89 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1890,14 +1897,14 @@ ui/icons/icon-grid index: -1 ui/icons/icon-hold rotate: false - xy: 195, 85 + xy: 362, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-holdDelete rotate: false - xy: 207, 85 + xy: 374, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1909,9 +1916,16 @@ ui/icons/icon-home orig: 14, 14 offset: 0, 0 index: -1 +ui/icons/icon-host + rotate: false + xy: 631, 204 + size: 14, 14 + orig: 14, 14 + offset: 0, 0 + index: -1 ui/icons/icon-info rotate: false - xy: 219, 85 + xy: 386, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1925,7 +1939,7 @@ ui/icons/icon-line index: -1 ui/icons/icon-load rotate: false - xy: 631, 204 + xy: 647, 227 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -1953,21 +1967,21 @@ ui/icons/icon-loading index: -1 ui/icons/icon-menu rotate: false - xy: 231, 85 + xy: 398, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-none rotate: false - xy: 243, 85 + xy: 410, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-pause rotate: false - xy: 255, 85 + xy: 422, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -1988,35 +2002,35 @@ ui/icons/icon-pick index: -1 ui/icons/icon-play rotate: false - xy: 267, 85 + xy: 434, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-play-2 rotate: false - xy: 647, 227 + xy: 647, 211 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-power rotate: false - xy: 279, 85 + xy: 446, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-production rotate: false - xy: 291, 85 + xy: 458, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-quit rotate: false - xy: 647, 211 + xy: 647, 195 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2030,7 +2044,7 @@ ui/icons/icon-redo index: -1 ui/icons/icon-rename rotate: false - xy: 647, 195 + xy: 631, 188 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2044,35 +2058,35 @@ ui/icons/icon-resize index: -1 ui/icons/icon-rotate rotate: false - xy: 631, 188 + xy: 647, 179 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-rotate-arrow rotate: false - xy: 647, 179 + xy: 722, 411 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-rotate-left rotate: false - xy: 722, 411 + xy: 722, 395 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-rotate-right rotate: false - xy: 722, 395 + xy: 724, 365 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-save rotate: false - xy: 724, 365 + xy: 724, 349 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2093,7 +2107,7 @@ ui/icons/icon-save-map index: -1 ui/icons/icon-settings rotate: false - xy: 303, 89 + xy: 470, 93 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -2107,35 +2121,35 @@ ui/icons/icon-terrain index: -1 ui/icons/icon-tools rotate: false - xy: 724, 349 + xy: 724, 333 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-touch rotate: false - xy: 315, 89 + xy: 492, 92 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-touchDelete rotate: false - xy: 327, 89 + xy: 504, 92 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 ui/icons/icon-trash rotate: false - xy: 724, 333 + xy: 724, 317 size: 14, 14 orig: 14, 14 offset: 0, 0 index: -1 ui/icons/icon-tutorial rotate: false - xy: 724, 317 + xy: 738, 412 size: 14, 14 orig: 14, 14 offset: 0, 0 @@ -2149,7 +2163,7 @@ ui/icons/icon-undo index: -1 ui/icons/icon-weapon rotate: false - xy: 339, 89 + xy: 516, 92 size: 10, 10 orig: 10, 10 offset: 0, 0 @@ -2227,7 +2241,7 @@ ui/scroll-knob-vertical-black index: -1 ui/selection rotate: false - xy: 524, 117 + xy: 592, 284 size: 1, 1 orig: 1, 1 offset: 0, 0 @@ -2306,7 +2320,7 @@ ui/textfield-over index: -1 ui/white rotate: false - xy: 535, 188 + xy: 738, 391 size: 3, 3 orig: 3, 3 offset: 0, 0 @@ -2329,42 +2343,42 @@ ui/window-empty index: -1 weapons/blaster rotate: false - xy: 265, 75 + xy: 851, 463 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/flamer rotate: false - xy: 884, 485 + xy: 911, 453 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/mortar rotate: false - xy: 891, 455 + xy: 891, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/multigun rotate: false - xy: 911, 475 + xy: 911, 443 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/railgun rotate: false - xy: 951, 455 + xy: 740, 341 size: 8, 8 orig: 8, 8 offset: 0, 0 index: -1 weapons/triblaster rotate: false - xy: 921, 445 + xy: 538, 96 size: 8, 8 orig: 8, 8 offset: 0, 0 diff --git a/core/assets/sprites/sprites.png b/core/assets/sprites/sprites.png index 0847a11908..c0118c8038 100644 Binary files a/core/assets/sprites/sprites.png and b/core/assets/sprites/sprites.png differ diff --git a/core/assets/ui/uiskin.json b/core/assets/ui/uiskin.json index 8c6e025c8f..ab6dec7b92 100644 --- a/core/assets/ui/uiskin.json +++ b/core/assets/ui/uiskin.json @@ -46,7 +46,7 @@ io.anuke.ucore.scene.ui.TextButton$TextButtonStyle: { toggle: {font: default-font, fontColor: white, checked: button-down, down: button-down, up: button, over: button-over, disabled: button, disabledFontColor: grey } }, io.anuke.ucore.scene.ui.ImageButton$ImageButtonStyle: { - default: {down: button-down, up: button, over: button-over }, + default: {down: button-down, up: button, over: button-over, imageDisabledColor: lightgray, imageUpColor: white }, empty: { imageDownColor: accent, imageUpColor: white}, emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: lightgray}, static: {up: button }, diff --git a/core/src/io/anuke/mindustry/Mindustry.java b/core/src/io/anuke/mindustry/Mindustry.java index 00bb317a6a..edbc35f299 100644 --- a/core/src/io/anuke/mindustry/Mindustry.java +++ b/core/src/io/anuke/mindustry/Mindustry.java @@ -1,16 +1,13 @@ package io.anuke.mindustry; -import java.util.Date; -import java.util.Locale; - import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; - import com.badlogic.gdx.utils.I18NBundle; import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.io.PlatformFunction; +import io.anuke.mindustry.net.Net; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.blocks.*; import io.anuke.ucore.UCore; @@ -21,6 +18,9 @@ import io.anuke.ucore.function.Callable; import io.anuke.ucore.modules.ModuleCore; import io.anuke.ucore.scene.ui.TextField; +import java.util.Date; +import java.util.Locale; + public class Mindustry extends ModuleCore { public static Callable donationsCallable; public static boolean hasDiscord = true; @@ -90,16 +90,9 @@ public class Mindustry extends ModuleCore { @Override public void render(){ + super.render(); - try{ - super.render(); - }catch (RuntimeException e){ - //TODO display error log - //Gdx.app.getClipboard().setContents(e.getMessage()); - throw e; - } - - if(!GameState.is(State.paused)){ + if(!GameState.is(State.paused) || Net.active()){ Timers.update(); } diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index fda0b73ec8..572770fa43 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -20,7 +20,7 @@ public class Vars{ //respawn time in frames public static final float respawnduration = 60*4; //time between waves in frames (on normal mode) - public static final float wavespace = 60*60*(android ? 1 : 1); + public static final float wavespace = 60*60*(android ? 1 : 1); //TODO revert //waves can last no longer than 3 minutes, otherwise the next one spawns public static final float maxwavespace = 60*60*4f; //advance time the pathfinding starts at @@ -43,6 +43,7 @@ public class Vars{ public static final int zoomScale = Math.round(Unit.dp.scl(1)); //if true, player speed will be increased, massive amounts of resources will be given on start, and other debug options will be available public static boolean debug = false; + public static boolean debugNet = false; //whether the player can clip through walls public static boolean noclip = false; //whether to draw chunk borders diff --git a/core/src/io/anuke/mindustry/ai/Pathfind.java b/core/src/io/anuke/mindustry/ai/Pathfind.java index 63d64d9250..e89ef2ea99 100644 --- a/core/src/io/anuke/mindustry/ai/Pathfind.java +++ b/core/src/io/anuke/mindustry/ai/Pathfind.java @@ -8,6 +8,7 @@ import com.badlogic.gdx.math.Vector2; import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.net.Net; import io.anuke.mindustry.world.SpawnPoint; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.util.Angles; @@ -101,20 +102,28 @@ public class Pathfind{ } public void update(){ + int index = 0; + for(SpawnPoint point : Vars.control.getSpawnPoints()){ if(!point.request.pathFound){ try{ if(point.finder.search(point.request, ms)){ smoother.smoothPath(point.path); point.pathTiles = point.path.nodes.toArray(Tile.class); - point.tempTiles = point.path.nodes.toArray(Tile.class); + + if(Net.active() && Net.server()){ + Vars.netServer.handlePathFound(index, point.pathTiles); + } } }catch (ArrayIndexOutOfBoundsException e){ //no path point.request.pathFound = true; } } + + index ++; } + } public boolean finishedUpdating(){ @@ -133,7 +142,6 @@ public class Pathfind{ point.path.clear(); point.pathTiles = null; - point.tempTiles = null; point.request = new PathFinderRequest(point.start, Vars.control.getCore(), heuristic, point.path); point.request.statusChanged = true; //IMPORTANT! @@ -141,15 +149,15 @@ public class Pathfind{ } void findNode(Enemy enemy){ - if(enemy.spawn >= Vars.control.getSpawnPoints().size){ - enemy.spawn = 0; + if(enemy.lane >= Vars.control.getSpawnPoints().size){ + enemy.lane = 0; } - if(Vars.control.getSpawnPoints().get(enemy.spawn).pathTiles == null){ + if(Vars.control.getSpawnPoints().get(enemy.lane).pathTiles == null){ return; } - enemy.path = Vars.control.getSpawnPoints().get(enemy.spawn).pathTiles; + enemy.path = Vars.control.getSpawnPoints().get(enemy.lane).pathTiles; int closest = findClosest(enemy.path, enemy.x, enemy.y); diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index d2b715f9ee..0abc4a9cea 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -49,7 +49,7 @@ public class Control extends Module{ final Array weapons = new Array<>(); final int[] items = new int[Item.getAllItems().size]; - public final EntityGroup enemyGroup = Entities.addGroup(Enemy.class); + public final EntityGroup enemyGroup = Entities.addGroup(Enemy.class).enableMapping(); public final EntityGroup tileGroup = Entities.addGroup(TileEntity.class, false); public final EntityGroup bulletGroup = Entities.addGroup(Bullet.class); public final EntityGroup shieldGroup = Entities.addGroup(Shield.class); @@ -187,7 +187,13 @@ public class Control extends Module{ for(int i = 0; i < Vars.saveSlots; i ++){ Settings.defaults("save-" + i + "-autosave", true); Settings.defaults("save-" + i + "-name", "untitled"); + Settings.defaults("save-" + i + "-data", "empty"); } + + Settings.defaultList( + "ip", "localhost", + "port", Vars.port+"" + ); Settings.loadAll("io.anuke.moment"); @@ -196,6 +202,7 @@ public class Control extends Module{ } player = new Player(); + player.isAndroid = Vars.android; player.isLocal = true; spawns = WaveCreator.getSpawns(); @@ -223,9 +230,8 @@ public class Control extends Module{ wavetime = waveSpacing(); Entities.clear(); enemies = 0; - - if(!android) - player.add(); + + player.add(); player.heal(); clearItems(); @@ -355,10 +361,11 @@ public class Control extends Module{ try{ Enemy enemy = ClassReflection.newInstance(spawn.type); enemy.set(tile.worldx() + Mathf.range(range), tile.worldy() + Mathf.range(range)); - enemy.spawn = fl; + enemy.lane = fl; enemy.tier = spawn.tier(wave, fl); + enemy.add(); + Effects.effect(Fx.spawn, enemy); - enemy.add(enemyGroup); Vars.netServer.handleEnemySpawn(enemy); @@ -404,6 +411,10 @@ public class Control extends Module{ } public void coreDestroyed(){ + if(Net.active() && Net.server()){ + Net.closeServer(); + } + Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y); Sounds.play("corexplode"); for(int i = 0; i < 16; i ++){ @@ -619,7 +630,7 @@ public class Control extends Module{ if(!GameState.is(State.menu)){ input.update(); - if(Inputs.keyTap("pause") && !ui.isGameOver() && (GameState.is(State.paused) || GameState.is(State.playing))){ + if(Inputs.keyTap("pause") && !ui.isGameOver() && !Net.active() && (GameState.is(State.paused) || GameState.is(State.playing))){ GameState.set(GameState.is(State.playing) ? State.paused : State.playing); } @@ -633,7 +644,7 @@ public class Control extends Module{ } } - if(!GameState.is(State.paused)){ + if(!GameState.is(State.paused) || Net.active()){ if(respawntime > 0){ @@ -657,7 +668,7 @@ public class Control extends Module{ if(enemies <= 0){ wavetime -= delta(); - if(lastUpdated < wave + 1 && wavetime < Vars.aheadPathfinding){ //start updatingbeforehand + if(lastUpdated < wave + 1 && wavetime < Vars.aheadPathfinding){ //start updating beforehand world.pathfinder().updatePath(); lastUpdated = wave + 1; } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 5f581b30ad..6cb6099246 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -1,25 +1,41 @@ package io.anuke.mindustry.core; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.utils.TimeUtils; +import com.badlogic.gdx.utils.reflect.ClassReflection; +import com.badlogic.gdx.utils.reflect.ReflectionException; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.entities.Bullet; +import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.graphics.Fx; +import io.anuke.mindustry.io.NetworkIO; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Packets.*; +import io.anuke.mindustry.net.Syncable; +import io.anuke.mindustry.net.Syncable.Interpolator; import io.anuke.mindustry.resource.Weapon; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; import io.anuke.ucore.UCore; +import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.BaseBulletType; +import io.anuke.ucore.entities.Entity; import io.anuke.ucore.modules.Module; +import java.io.DataInputStream; +import java.io.IOException; import java.util.Arrays; public class NetClient extends Module { boolean connecting = false; boolean gotEntities = false; - float playerSyncTime = 3; + float playerSyncTime = 2; + float dataTimeout = 60*10; public NetClient(){ @@ -30,6 +46,19 @@ public class NetClient extends Module { Vars.ui.hideLoading(); Vars.ui.showLoading("$text.connecting.data"); }); + + ConnectPacket c = new ConnectPacket(); + c.name = UCore.getProperty("user.name"); + c.android = Vars.android; + Net.send(c, SendMode.tcp); + + Timers.runTask(dataTimeout, () -> { + if(!gotEntities){ + Gdx.app.error("Mindustry", "Failed to load data!"); + Vars.ui.hideLoading(); + Net.disconnect(); + } + }); }); Net.handle(Disconnect.class, packet -> { @@ -38,6 +67,8 @@ public class NetClient extends Module { Vars.ui.hideLoading(); }); + GameState.set(State.menu); + Vars.ui.showError("$text.disconnect"); connecting = false; }); @@ -46,7 +77,8 @@ public class NetClient extends Module { Net.handle(WorldData.class, data -> { Gdx.app.postRunnable(() -> { UCore.log("Recieved world data: " + data.stream.available() + " bytes."); - SaveIO.load(data.stream); + NetworkIO.load(data.stream); + Vars.player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2); GameState.set(State.playing); connecting = false; @@ -58,7 +90,7 @@ public class NetClient extends Module { Net.handle(EntityDataPacket.class, data -> { Gdx.app.postRunnable(() -> { - Timers.run(10f, () -> { //TODO hack + Timers.run(10f, () -> { //TODO hack. should only run once world data is recieved Vars.control.playerGroup.remap(Vars.player, data.playerid); for (Player player : data.players) { @@ -76,11 +108,26 @@ public class NetClient extends Module { Net.handle(SyncPacket.class, packet -> { if(!gotEntities) return; + //TODO awful code for(int i = 0; i < packet.ids.length; i ++){ int id = packet.ids[i]; if(id != Vars.player.id){ - Player player = Vars.control.playerGroup.getByID(id); - player.getInterpolator().type.read(player, packet.data[i]); + Entity entity = null; + if(i >= packet.enemyStart){ + entity = Vars.control.enemyGroup.getByID(id); + }else { + entity = Vars.control.playerGroup.getByID(id); + } + + Syncable sync = ((Syncable)entity); + + if(sync == null){ + Gdx.app.error("Mindustry", "Unknown entity ID: " + id + " " + (i >= packet.enemyStart ? "(enemy)" : "(player)")); + continue; + } + + //augh + ((Interpolator)sync.getInterpolator()).type.read(entity, packet.data[i]); } } }); @@ -92,25 +139,120 @@ public class NetClient extends Module { weapon.shoot(player, packet.x, packet.y, packet.rotation); }); - Net.handleServer(PlacePacket.class, packet -> { + Net.handle(PlacePacket.class, packet -> { Vars.control.input.placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false); }); - Net.handleServer(BreakPacket.class, packet -> { + Net.handle(BreakPacket.class, packet -> { Vars.control.input.breakBlockInternal(packet.x, packet.y, false); }); - Net.handleServer(StateSyncPacket.class, packet -> { + Net.handle(StateSyncPacket.class, packet -> { //TODO replace with arraycopy() for(int i = 0; i < packet.items.length; i ++){ Vars.control.items[i] = packet.items[i]; } Vars.control.setWaveData(packet.enemies, packet.wave, packet.countdown); + + Timers.resetTime(packet.time + (float)(TimeUtils.timeSinceMillis(packet.timestamp) / 1000.0 * 60.0)); + + Gdx.app.postRunnable(() -> { + Vars.ui.updateItems(); + }); + }); + + Net.handle(EnemySpawnPacket.class, spawn -> { + Gdx.app.postRunnable(() -> { + try{ + Enemy enemy = ClassReflection.newInstance(spawn.type); + enemy.set(spawn.x, spawn.y); + enemy.tier = spawn.tier; + enemy.lane = spawn.lane; + enemy.id = spawn.id; + enemy.add(); + + Effects.effect(Fx.spawn, enemy); + }catch (ReflectionException e){ + throw new RuntimeException(e); + } + }); + }); + + Net.handle(EnemyDeathPacket.class, spawn -> { + Gdx.app.postRunnable(() -> { + Enemy enemy = Vars.control.enemyGroup.getByID(spawn.id); + if (enemy != null) enemy.onDeath(); + }); + }); + + Net.handle(PathPacket.class, packet -> { + Tile[] tiles = new Tile[packet.path.length]; + for(int i = 0; i < tiles.length; i ++){ + int c = packet.path[i]; + tiles[i] = Vars.world.tile(c % Vars.world.width(), c / Vars.world.width()); + } + Vars.control.spawnpoints.get(packet.index).pathTiles = tiles; + }); + + Net.handle(BulletPacket.class, packet -> { + //TODO shoot effects for enemies, clientside as well as serverside + BulletType type = (BulletType) BaseBulletType.getByID(packet.type); + Entity owner = Vars.control.enemyGroup.getByID(packet.owner); + Bullet bullet = new Bullet(type, owner, packet.x, packet.y, packet.angle).add(); + }); + + Net.handle(BlockDestroyPacket.class, packet -> { + Tile tile = Vars.world.tile(packet.position % Vars.world.width(), packet.position / Vars.world.width()); + if(tile.entity != null){ + tile.entity.onDeath(true); + } + }); + + Net.handle(BlockUpdatePacket.class, packet -> { + Tile tile = Vars.world.tile(packet.position % Vars.world.width(), packet.position / Vars.world.width()); + if(tile.entity != null){ + tile.entity.health = packet.health; + } + }); + + Net.handle(BlockSyncPacket.class, packet -> { + //TODO implementation, load data... + DataInputStream stream = new DataInputStream(packet.stream); + + try{ + while(stream.available() > 0){ + int pos = stream.readInt(); + + Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); + + byte times = stream.readByte(); + + for(int i = 0; i < times; i ++){ + tile.entity.timer.getTimes()[i] = stream.readFloat(); + } + + tile.entity.read(stream); + } + }catch (IOException e){ + e.printStackTrace(); + } + }); + + Net.handle(DisconnectPacket.class, packet -> { + Player player = Vars.control.playerGroup.getByID(packet.playerid); + + if(player != null){ + player.remove(); + } + }); + + Net.handle(Player.class, player -> { + player.add(); }); } public void update(){ - if(!Net.client()) return; + if(!Net.client() || !Net.active()) return; if(!GameState.is(State.menu) && Net.active()){ sync(); @@ -149,7 +291,7 @@ public class NetClient extends Module { if(Timers.get("syncPlayer", playerSyncTime)){ PositionPacket packet = new PositionPacket(); packet.data = Vars.player.getInterpolator().type.write(Vars.player); - Net.send(packet, SendMode.tcp); //TODO udp instead? + Net.send(packet, SendMode.udp); //TODO udp instead? } } } diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 00509eba94..f8f8829272 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -2,12 +2,16 @@ package io.anuke.mindustry.core; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.IntMap; +import com.badlogic.gdx.utils.TimeUtils; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.io.NetworkIO; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Packets.*; @@ -15,55 +19,90 @@ import io.anuke.mindustry.resource.ItemStack; import io.anuke.mindustry.resource.Recipe; import io.anuke.mindustry.resource.Weapon; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; import io.anuke.ucore.UCore; import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.Entity; import io.anuke.ucore.modules.Module; +import io.anuke.ucore.util.Bundles; +import io.anuke.ucore.util.Mathf; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; +import java.io.IOException; import java.util.Arrays; public class NetServer extends Module{ + /**Maps connection IDs to players.*/ IntMap connections = new IntMap<>(); - float serverSyncTime = 4, itemSyncTime = 20, blockSyncTime = 120; + float serverSyncTime = 4, itemSyncTime = 10, blockSyncTime = 120; public NetServer(){ - Net.handleServer(Connect.class, packet -> { - UCore.log("Sending world data to client (ID="+packet.id+"/"+packet.addressTCP+")"); + Net.handleServer(Connect.class, connect -> { + UCore.log("Connection found: " + connect.addressTCP); + }); + + Net.handleServer(ConnectPacket.class, packet -> { + int id = Net.getLastConnection(); + + UCore.log("Sending world data to client (ID="+id+")"); WorldData data = new WorldData(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); - SaveIO.write(stream); + NetworkIO.write(stream); UCore.log("Packed " + stream.size() + " uncompressed bytes of data."); //TODO compress and uncompress when sending data.stream = new ByteArrayInputStream(stream.toByteArray()); - Net.sendStream(packet.id, data); + Net.sendStream(id, data); Gdx.app.postRunnable(() -> { + Vars.ui.showInfo("$text.server.connected"); + EntityDataPacket dp = new EntityDataPacket(); Player player = new Player(); - player.clientid = packet.id; - player.set(Vars.player.x, Vars.player.y); - player.placerot = Vars.player.placerot; + player.clientid = id; + player.name = packet.name; + player.isAndroid = packet.android; + player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2); player.add(); - connections.put(packet.id, player); + connections.put(id, player); dp.playerid = player.id; dp.players = Vars.control.playerGroup.all().toArray(Player.class); UCore.log("Sending entities: " + Arrays.toString(dp.players)); - Net.sendTo(packet.id, dp, SendMode.tcp); + Net.sendExcept(id, player, SendMode.tcp); + + Net.sendTo(id, dp, SendMode.tcp); }); }); + Net.handleServer(Disconnect.class, packet -> { + Player player = connections.get(packet.id); + + if(player == null) { + Gdx.app.postRunnable(() -> Vars.ui.showInfo(Bundles.format("text.server.disconnected", ""))); + return; + } + + Gdx.app.postRunnable(() -> Vars.ui.showInfo(Bundles.format("text.server.disconnected", player.name))); + + player.remove(); + + DisconnectPacket dc = new DisconnectPacket(); + dc.playerid = player.id; + + Net.send(dc, SendMode.tcp); + }); + Net.handleServer(PositionPacket.class, pos -> { Player player = connections.get(Net.getLastConnection()); player.getInterpolator().type.read(player, pos.data); @@ -112,8 +151,58 @@ public class NetServer extends Module{ Net.send(packet, SendMode.udp); } - public void handleEnemySpawn(Enemy enemy){ + public void handleBullet(BulletType type, Entity owner, float x, float y, float angle, short damage){ + BulletPacket packet = new BulletPacket(); + packet.x = x; + packet.y = y; + packet.angle = angle; + packet.damage = damage; + packet.owner = owner.id; + packet.type = type.id; + Net.send(packet, SendMode.udp); + } + public void handlePathFound(int index, Tile[] tiles){ + PathPacket packet = new PathPacket(); + int[] out = new int[tiles.length]; + + for(int p = 0; p < out.length; p ++){ + out[p] = tiles[p].packedPosition(); + } + + packet.path = out; + packet.index = (byte)index; + Net.send(packet, SendMode.tcp); + } + + public void handleEnemySpawn(Enemy enemy){ + EnemySpawnPacket packet = new EnemySpawnPacket(); + packet.type = enemy.getClass(); + packet.lane = (byte)enemy.lane; + packet.tier = (byte)enemy.tier; + packet.x = enemy.x; + packet.y = enemy.y; + packet.id = enemy.id; + Net.send(packet, SendMode.tcp); + } + + public void handleEnemyDeath(Enemy enemy){ + EnemyDeathPacket packet = new EnemyDeathPacket(); + packet.id = enemy.id; + Net.send(packet, SendMode.tcp); + } + + public void handleBlockDestroyed(TileEntity entity){ + BlockDestroyPacket packet = new BlockDestroyPacket(); + packet.position = entity.tile.packedPosition(); + Net.send(packet, SendMode.tcp); + } + + public void handleBlockDamaged(TileEntity entity){ + BlockUpdatePacket packet = new BlockUpdatePacket(); + packet.health = entity.health; + packet.position = entity.tile.packedPosition(); + Net.send(packet, SendMode.udp); } public void update(){ @@ -130,11 +219,11 @@ public class NetServer extends Module{ if(Timers.get("serverSync", serverSyncTime)){ SyncPacket packet = new SyncPacket(); - int amount = Vars.control.playerGroup.amount(); + int amount = Vars.control.playerGroup.amount() + Vars.control.enemyGroup.amount(); packet.ids = new int[amount]; packet.data = new float[amount][0]; - int index = 0; + short index = 0; for(Player player : Vars.control.playerGroup.all()){ float[] out = player.getInterpolator().type.write(player); @@ -144,6 +233,16 @@ public class NetServer extends Module{ index ++; } + packet.enemyStart = index; + + for(Enemy enemy : Vars.control.enemyGroup.all()){ + float[] out = enemy.getInterpolator().type.write(enemy); + packet.data[index] = out; + packet.ids[index] = enemy.id; + + index ++; + } + Net.send(packet, SendMode.udp); } @@ -153,25 +252,71 @@ public class NetServer extends Module{ packet.countdown = Vars.control.getWaveCountdown(); packet.enemies = Vars.control.getEnemiesRemaining(); packet.wave = Vars.control.getWave(); + packet.time = Timers.time(); + packet.timestamp = TimeUtils.millis(); Net.send(packet, SendMode.udp); } if(Timers.get("serverBlockSync", blockSyncTime)){ - BlockSyncPacket packet = new BlockSyncPacket(); + IntArray connections = Net.getConnections(); + for(int i = 0; i < connections.size; i ++){ + int id = connections.get(i); + Player player = this.connections.get(id); + if(player == null) continue; + int x = Mathf.scl2(player.x, Vars.tilesize); + int y = Mathf.scl2(player.y, Vars.tilesize); + int w = 16; + int h = 12; + sendBlockSync(id, x, y, w, h); + } + + //TODO sync to each player entity } } - public void sendBlockSync(int client){ + public void sendBlockSync(int client, int x, int y, int viewx, int viewy){ BlockSyncPacket packet = new BlockSyncPacket(); - ByteArrayOutputStream bs = new ByteArrayOutputStream(); - DataOutputStream stream = new DataOutputStream(bs); + try { + DataOutputStream stream = new DataOutputStream(bs); + for (int rx = -viewx / 2; rx <= viewx / 2; rx++) { + for (int ry = -viewy / 2; ry <= viewy / 2; ry++) { + Tile tile = Vars.world.tile(x + rx, y + ry); + + if (tile == null || tile.entity == null) continue; + + stream.writeInt(tile.packedPosition()); + byte times = 0; + + for(; times < tile.entity.timer.getTimes().length; times ++){ + if(tile.entity.timer.getTimes()[times] <= 1f){ + break; + } + } + + stream.writeByte(times); + + for(int i = 0; i < times; i ++){ + stream.writeFloat(tile.entity.timer.getTimes()[i]); + } + + tile.entity.write(stream); + } + } + + }catch (IOException e){ + throw new RuntimeException(e); + } + + //TODO finish packet.stream = new ByteArrayInputStream(bs.toByteArray()); + + Net.sendStream(client, packet); } } diff --git a/core/src/io/anuke/mindustry/core/Renderer.java b/core/src/io/anuke/mindustry/core/Renderer.java index 4d9afffd38..9236def916 100644 --- a/core/src/io/anuke/mindustry/core/Renderer.java +++ b/core/src/io/anuke/mindustry/core/Renderer.java @@ -418,10 +418,8 @@ public class Renderer extends RendererModule{ drawHealth(entity); } - if(!Vars.android && Vars.showPlayer) { - for(Player player : Vars.control.playerGroup.all()){ - if(!player.isDead()) drawHealth(player); - } + for(Player player : Vars.control.playerGroup.all()){ + if(!player.isDead() && !player.isAndroid) drawHealth(player); } } } diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index ed174a83fb..28461457f8 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -24,6 +24,7 @@ import io.anuke.ucore.function.Listenable; import io.anuke.ucore.modules.SceneModule; import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.Skin; +import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.builders.build; import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.table; @@ -177,27 +178,6 @@ public class UI extends SceneModule{ editor = new MapEditor(); editorDialog = new MapEditorDialog(editor); } - - join = new FloatingDialog("$text.joingame.title"); - join.content().add("$text.joingame.ip").left(); - join.content().addField("localhost", text -> lastip = text).size(180f, 54f); - join.content().row(); - join.content().add("$text.server.port").left(); - join.content().addField(Vars.port + "", new DigitsOnlyFilter(), text -> lastport = Strings.parseInt(text)).size(180f, 54f); - join.buttons().defaults().size(140f, 60f).pad(4f); - join.buttons().addButton("$text.cancel", join::hide); - join.buttons().addButton("$text.ok", () -> { - showLoading("$text.connecting"); - - Timers.runTask(2f, () -> { - try{ - Net.connect(lastip, lastport); - }catch (IOException e) { - showError(Bundles.format("text.connectfail", Strings.parseException(e, false))); - hideLoading(); - } - }); - }).disabled(b -> lastip.isEmpty() || lastport == Integer.MIN_VALUE); settingserror = new Dialog("Warning", "dialog"); settingserror.content().add("[crimson]Failed to access local storage.\nSettings will not be saved."); @@ -207,6 +187,8 @@ public class UI extends SceneModule{ gameerror = new Dialog("$text.error.crashtitle", "dialog"); gameerror.content().labelWrap("$text.error.crashmessage").width(600f).pad(10f); gameerror.buttons().addButton("#text.ok", gameerror::hide).size(200f, 50); + + join = new JoinDialog(); discord = new Dialog("Discord", "dialog"); discord.content().margin(12f); @@ -258,7 +240,7 @@ public class UI extends SceneModule{ prefs.hidden(()->{ if(!GameState.is(State.menu)){ - if(!wasPaused) + if(!wasPaused || Net.active()) GameState.set(State.playing); } }); @@ -269,7 +251,7 @@ public class UI extends SceneModule{ if(menu.getScene() != null){ wasPaused = menu.wasPaused; } - GameState.set(State.paused); + if(!Net.active()) GameState.set(State.paused); menu.hide(); } }); @@ -467,6 +449,25 @@ public class UI extends SceneModule{ if(tooltip != null) tooltip.hide(); } + + public void showInfo(String info){ + scene.table().add(info).get().getParent().actions(Actions.fadeOut(4f), Actions.removeActor()); + } + + public void showHostServer(){ + showTextInput("$text.hostserver", "$text.server.port", Vars.port + "", new DigitsOnlyFilter(), text -> { + int result = Strings.parseInt(text); + if(result == Integer.MIN_VALUE || result >= 65535){ + Vars.ui.showError("$text.server.invalidport"); + }else{ + try{ + Net.host(result); + }catch (IOException e){ + Vars.ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false))); + } + } + }); + } public void showAbout(){ about.show(); diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index 8c5cd7195f..1cff20a251 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -1,25 +1,31 @@ package io.anuke.mindustry.core; -import static io.anuke.mindustry.Vars.*; - import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; - import io.anuke.mindustry.Vars; import io.anuke.mindustry.ai.Pathfind; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.io.Maps; -import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Generator; +import io.anuke.mindustry.world.Map; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Blocks; +import io.anuke.mindustry.world.blocks.DistributionBlocks; +import io.anuke.mindustry.world.blocks.ProductionBlocks; +import io.anuke.mindustry.world.blocks.WeaponBlocks; import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entity; import io.anuke.ucore.modules.Module; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Tmp; +import static io.anuke.mindustry.Vars.*; + public class World extends Module{ private int seed; @@ -36,7 +42,8 @@ public class World extends Module{ @Override public void update(){ - pathfind.update(); + if(!(Net.active() && Net.client())) + pathfind.update(); } @Override diff --git a/core/src/io/anuke/mindustry/entities/Player.java b/core/src/io/anuke/mindustry/entities/Player.java index 8851e703e0..10543d8e95 100644 --- a/core/src/io/anuke/mindustry/entities/Player.java +++ b/core/src/io/anuke/mindustry/entities/Player.java @@ -11,6 +11,7 @@ import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.ucore.core.*; import io.anuke.ucore.entities.DestructibleEntity; +import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Mathf; @@ -19,10 +20,14 @@ import static io.anuke.mindustry.Vars.*; public class Player extends DestructibleEntity implements Syncable{ private static final float speed = 1.1f; private static final float dashSpeed = 1.8f; - + + public String name = "player name"; public transient Weapon weapon = Weapon.blaster; public Mech mech = Mech.standard; public float angle; + public boolean isAndroid; + + public transient float targetAngle = 0f; public transient int clientid; public transient boolean isLocal = false; @@ -43,15 +48,20 @@ public class Player extends DestructibleEntity implements Syncable{ } @Override - public Interpolator getInterpolator() { + public Interpolator getInterpolator() { return inter; } @Override public void damage(int amount){ - if(!Vars.debug && !Vars.android) + if(!Vars.debug) super.damage(amount); } + + @Override + public boolean collides(SolidEntity other){ + return super.collides(other) && !isAndroid; + } @Override public void onDeath(){ @@ -79,20 +89,26 @@ public class Player extends DestructibleEntity implements Syncable{ @Override public void draw(){ - if(Vars.debug && (!Vars.showPlayer || !Vars.showUI)) return; + if(isAndroid && isLocal){ + angle = Mathf.lerpAngDelta(angle, targetAngle, 0.2f); + } + + if((Vars.debug && (!Vars.showPlayer || !Vars.showUI)) || (isAndroid && isLocal)) return; + + String part = isAndroid ? "ship" : "mech"; if(Vars.snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate")){ - Draw.rect("mech-"+mech.name(), (int)x, (int)y, angle-90); + Draw.rect(part+"-"+mech.name(), (int)x, (int)y, angle-90); }else{ - Draw.rect("mech-"+mech.name(), x, y, angle-90); + Draw.rect(part+"-"+mech.name(), x, y, angle-90); } } @Override public void update(){ - if(!isLocal){ - if(!isDead()) inter.update(this); + if(!isLocal || isAndroid){ + if(!isDead() && !isLocal) inter.update(this); return; } @@ -151,4 +167,9 @@ public class Player extends DestructibleEntity implements Syncable{ public Player add(){ return add(Vars.control.playerGroup); } + + @Override + public String toString() { + return "Player{" + id + ", android=" + isAndroid + ", local=" + isLocal + ", " + x + ", " + y + "}\n"; + } } diff --git a/core/src/io/anuke/mindustry/entities/TileEntity.java b/core/src/io/anuke/mindustry/entities/TileEntity.java index 071134313a..21ea1d52b1 100644 --- a/core/src/io/anuke/mindustry/entities/TileEntity.java +++ b/core/src/io/anuke/mindustry/entities/TileEntity.java @@ -1,12 +1,9 @@ package io.anuke.mindustry.entities; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.graphics.Fx; +import io.anuke.mindustry.net.Net; import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; @@ -18,6 +15,10 @@ import io.anuke.ucore.entities.Entity; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Timer; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + public class TileEntity extends Entity{ public Tile tile; public int[] items = new int[Item.getAllItems().size]; @@ -54,19 +55,28 @@ public class TileEntity extends Entity{ } public void onDeath(){ - - if(tile.block() == ProductionBlocks.core){ - Vars.control.coreDestroyed(); + onDeath(false); + } + + public void onDeath(boolean force){ + if(Net.active() && Net.server()){ + Vars.netServer.handleBlockDestroyed(this); } - if(!dead) { - dead = true; - Block block = tile.block(); + if(!Net.active() || Net.server() || force){ + if(tile.block() == ProductionBlocks.core){ + Vars.control.coreDestroyed(); + } - block.onDestroyed(tile); + if(!dead) { + dead = true; + Block block = tile.block(); - Vars.world.removeBlock(tile); - remove(); + block.onDestroyed(tile); + + Vars.world.removeBlock(tile); + remove(); + } } } @@ -80,6 +90,10 @@ public class TileEntity extends Entity{ int amount = tile.block().handleDamage(tile, damage); health -= amount; if(health <= 0) onDeath(); + + if(Net.active() && Net.server()){ + Vars.netServer.handleBlockDamaged(this); + } } public boolean collide(Bullet other){ diff --git a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java index 60e901d038..95a0f13451 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java @@ -3,21 +3,32 @@ package io.anuke.mindustry.entities.enemies; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.reflect.ClassReflection; - import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.Bullet; +import io.anuke.mindustry.entities.BulletType; +import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Shaders; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.net.Syncable; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.UCore; -import io.anuke.ucore.core.*; -import io.anuke.ucore.entities.*; -import io.anuke.ucore.util.*; +import io.anuke.ucore.core.Draw; +import io.anuke.ucore.core.Effects; +import io.anuke.ucore.core.Graphics; +import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.DestructibleEntity; +import io.anuke.ucore.entities.Entities; +import io.anuke.ucore.entities.Entity; +import io.anuke.ucore.entities.SolidEntity; +import io.anuke.ucore.util.Angles; +import io.anuke.ucore.util.Mathf; +import io.anuke.ucore.util.Timer; +import io.anuke.ucore.util.Tmp; import static io.anuke.mindustry.Vars.world; -public class Enemy extends DestructibleEntity{ +public class Enemy extends DestructibleEntity implements Syncable{ public final static Color[] tierColors = { Color.valueOf("ffe451"), Color.valueOf("f48e20"), Color.valueOf("ff6757"), Color.valueOf("ff2d86") }; public final static int maxtier = 4; public final static float maxIdle = 60*1.5f; @@ -37,17 +48,19 @@ public class Enemy extends DestructibleEntity{ protected int damage; protected Enemy spawner; protected int spawned = 0; - protected float angle; protected boolean targetCore = false; protected boolean stopNearCore = true; protected float mass = 1f; protected String className; - + + protected Interpolator inter = new Interpolator(SyncType.enemy); + public float idletime = 0f; - public int spawn; + public int lane; public int node = -1; public Tile[] path; + public float angle; public float xvelocity, yvelocity; public Entity target; public int tier = 1; @@ -65,11 +78,20 @@ public class Enemy extends DestructibleEntity{ className = ClassReflection.getSimpleName(getClass()).toLowerCase(); } + public Interpolator getInterpolator() { + return inter; + } + public float drawSize(){ return 12; } void move(){ + if(Net.client() && Net.active()){ + inter.update(this); + return; + } + Tile core = Vars.control.getCore(); if(idletime > maxIdleLife){ @@ -152,9 +174,21 @@ public class Enemy extends DestructibleEntity{ } void shoot(BulletType bullet, float rotation){ - Angles.translation(angle + rotation, length); - Bullet out = new Bullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation).add(); - out.damage = (int) (damage * Vars.multiplier); + + if(!(Net.active() && Net.client())) { + Angles.translation(angle + rotation, length); + Bullet out = new Bullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation).add(); + out.damage = (int) (damage * Vars.multiplier); + onShoot(bullet, rotation); + + if(Net.active() && Net.server()){ + Vars.netServer.handleBullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation, (short) (damage * Vars.multiplier)); + } + } + } + + void onShoot(BulletType type, float rotation){ + } @Override @@ -184,6 +218,10 @@ public class Enemy extends DestructibleEntity{ Effects.sound("bang2", this); remove(); dead = true; + + if(Net.active() && Net.server()){ + Vars.netServer.handleEnemyDeath(this); + } } @Override @@ -258,7 +296,7 @@ public class Enemy extends DestructibleEntity{ } @Override - public T add(){ - return (T) add(Vars.control.enemyGroup); + public Enemy add(){ + return add(Vars.control.enemyGroup); } } diff --git a/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java b/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java index 45c9a659fa..2407149fa5 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java @@ -38,7 +38,7 @@ public class FortressEnemy extends Enemy{ Angles.translation(angle, 20f); FastEnemy enemy = new FastEnemy(); - enemy.spawn = spawn; + enemy.lane = lane; enemy.tier = this.tier; enemy.spawner = this; enemy.set(x + Angles.x(), y + Angles.y()); @@ -52,10 +52,9 @@ public class FortressEnemy extends Enemy{ speed = 0.001f; } } - - @Override - public void shoot(BulletType type){ - super.shoot(bullet); + + + void onShoot(BulletType type, float rotation){ Effects.effect(Fx.largeCannonShot, x + Angles.x(), y + Angles.y(), angle); Effects.shake(3f, 3f, this); } diff --git a/core/src/io/anuke/mindustry/input/GestureHandler.java b/core/src/io/anuke/mindustry/input/GestureHandler.java index 57eea040fb..1330783edb 100644 --- a/core/src/io/anuke/mindustry/input/GestureHandler.java +++ b/core/src/io/anuke/mindustry/input/GestureHandler.java @@ -1,15 +1,15 @@ package io.anuke.mindustry.input; -import static io.anuke.mindustry.Vars.*; - import com.badlogic.gdx.Gdx; import com.badlogic.gdx.input.GestureDetector.GestureAdapter; import com.badlogic.gdx.math.Vector2; - import io.anuke.mindustry.Vars; import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Inputs; import io.anuke.ucore.scene.ui.layout.Unit; +import io.anuke.ucore.util.Mathf; + +import static io.anuke.mindustry.Vars.*; public class GestureHandler extends GestureAdapter{ AndroidInput input; @@ -49,8 +49,10 @@ public class GestureHandler extends GestureAdapter{ if(!Vars.control.showCursor() && !(player.recipe != null && Vars.control.hasItems(player.recipe.requirements) && player.placeMode.lockCamera) && !(player.recipe == null && player.breakMode.lockCamera)){ - player.x -= deltaX*Core.camera.zoom/Core.cameraScale; - player.y += deltaY*Core.camera.zoom/Core.cameraScale; + float dx = deltaX*Core.camera.zoom/Core.cameraScale, dy = deltaY*Core.camera.zoom/Core.cameraScale; + player.x -= dx; + player.y += dy; + player.targetAngle = Mathf.atan2(dx, -dy); }else if(player.placeMode.lockCamera && (player.placeMode.pan && player.recipe != null)){ input.mousex += deltaX; input.mousey += deltaY; diff --git a/core/src/io/anuke/mindustry/io/NetworkIO.java b/core/src/io/anuke/mindustry/io/NetworkIO.java new file mode 100644 index 0000000000..4b8c60bf95 --- /dev/null +++ b/core/src/io/anuke/mindustry/io/NetworkIO.java @@ -0,0 +1,297 @@ +package io.anuke.mindustry.io; + +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.utils.TimeUtils; +import com.badlogic.gdx.utils.reflect.ClassReflection; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.GameMode; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Blocks; +import io.anuke.mindustry.world.blocks.types.BlockPart; +import io.anuke.mindustry.world.blocks.types.Rock; +import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.Entities; + +import java.io.*; + +import static io.anuke.mindustry.Vars.android; +import static io.anuke.mindustry.io.SaveIO.enemyIDs; +import static io.anuke.mindustry.io.SaveIO.idEnemies; + +public class NetworkIO { + private static final int fileVersionID = 13; + + public static void write(OutputStream os){ + + try(DataOutputStream stream = new DataOutputStream(os)){ + + //--META-- + stream.writeInt(fileVersionID); //version id + stream.writeFloat(Timers.time()); //timer time + stream.writeLong(TimeUtils.millis()); //timestamp + + //--GENERAL STATE-- + stream.writeByte(Vars.control.getMode().ordinal()); //gamemode + stream.writeByte(Vars.world.getMap().id); //map ID + + stream.writeInt(Vars.control.getWave()); //wave + stream.writeFloat(Vars.control.getWaveCountdown()); //wave countdown + + //--INVENTORY-- + + for(int i = 0; i < Vars.control.getItems().length; i ++){ + stream.writeInt(Vars.control.getItems()[i]); + } + + //--ENEMIES-- + + int totalEnemies = 0; + + for(Enemy entity : Vars.control.enemyGroup.all()){ + if(idEnemies.containsKey(entity.getClass())){ + totalEnemies ++; + } + } + + stream.writeInt(totalEnemies); //enemy amount + + for(Enemy enemy : Vars.control.enemyGroup.all()){ + if(idEnemies.containsKey(enemy.getClass())){ + stream.writeInt(enemy.id); + stream.writeByte(idEnemies.get(enemy.getClass())); //type + stream.writeByte(enemy.lane); //lane + stream.writeFloat(enemy.x); //x + stream.writeFloat(enemy.y); //y + stream.writeByte(enemy.tier); //tier + stream.writeShort(enemy.health); //health + stream.writeShort(enemy.node); //current node + } + } + + //--MAP DATA-- + + //seed + stream.writeInt(Vars.world.getSeed()); + + int totalblocks = 0; + int totalrocks = 0; + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + if(tile.breakable()){ + if(tile.block() instanceof Rock){ + totalrocks ++; + }else{ + totalblocks ++; + } + } + } + } + + //amount of rocks + stream.writeInt(totalrocks); + + //write all rocks + for(int x = 0; x < Vars.world.width(); x ++) { + for (int y = 0; y < Vars.world.height(); y++) { + Tile tile = Vars.world.tile(x, y); + + if (tile.block() instanceof Rock) { + stream.writeInt(tile.packedPosition()); + } + } + } + + //tile amount + stream.writeInt(totalblocks); + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + if(tile.breakable() && !(tile.block() instanceof Rock)){ + + stream.writeInt(x + y*Vars.world.width()); //tile pos + //TODO will break if block number gets over BYTE_MAX + stream.writeByte(tile.block().id); //block ID + + if(tile.block() instanceof BlockPart){ + stream.writeByte(tile.link); + } + + if(tile.entity != null){ + stream.writeByte(tile.getRotation()); //placerot + stream.writeShort(tile.entity.health); //health + + //items + for(int i = 0; i < tile.entity.items.length; i ++){ + stream.writeInt(tile.entity.items[i]); + } + + //timer data + + //amount of active timers + byte times = 0; + + for(; times < tile.entity.timer.getTimes().length; times ++){ + if(tile.entity.timer.getTimes()[times] <= 1){ + break; + } + } + + stream.writeByte(times); + + for(int i = 0; i < times; i ++){ + stream.writeFloat(tile.entity.timer.getTimes()[i]); + } + + tile.entity.write(stream); + } + } + } + } + + }catch (IOException e){ + throw new RuntimeException(e); + } + } + + public static void load(FileHandle file){ + load(file.read()); + } + + //TODO GWT support + public static void load(InputStream is){ + + try(DataInputStream stream = new DataInputStream(is)){ + + int version = stream.readInt(); + float timerTime = stream.readFloat(); + long timestamp = stream.readLong(); + + Timers.resetTime(timerTime + (TimeUtils.timeSinceMillis(timestamp) / 1000f) * 60f); + + if(version != fileVersionID){ + throw new RuntimeException("Save file version mismatch!"); + } + + //general state + byte mode = stream.readByte(); + byte mapid = stream.readByte(); + + int wave = stream.readInt(); + float wavetime = stream.readFloat(); + + Vars.control.setMode(GameMode.values()[mode]); + + //inventory + for(int i = 0; i < Vars.control.getItems().length; i ++){ + Vars.control.getItems()[i] = stream.readInt(); + } + + Vars.ui.updateItems(); + + //enemies + + Entities.clear(); + + int enemies = stream.readInt(); + + for(int i = 0; i < enemies; i ++){ + int id = stream.readInt(); + byte type = stream.readByte(); + int lane = stream.readByte(); + float x = stream.readFloat(); + float y = stream.readFloat(); + byte tier = stream.readByte(); + short health = stream.readShort(); + short node = stream.readShort(); + + try{ + Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); + enemy.id = id; + enemy.lane = lane; + enemy.health = health; + enemy.x = x; + enemy.y = y; + enemy.tier = tier; + enemy.node = node; + enemy.add(Vars.control.enemyGroup); + }catch (Exception e){ + throw new RuntimeException(e); + } + } + + Vars.control.setWaveData(enemies, wave, wavetime); + + Vars.player.add(); + + //map + + int seed = stream.readInt(); + + Vars.world.loadMap(Vars.world.maps().getMap(mapid), seed); + Vars.renderer.clearTiles(); + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + //remove breakables like rocks + if(tile.breakable()){ + Vars.world.tile(x, y).setBlock(Blocks.air); + } + } + } + + int rocks = stream.readInt(); + + for(int i = 0; i < rocks; i ++){ + int pos = stream.readInt(); + Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); + Block result = io.anuke.mindustry.world.Generator.rocks.get(tile.floor()); + if(result != null) tile.setBlock(result); + } + + int tiles = stream.readInt(); + + for(int i = 0; i < tiles; i ++){ + int pos = stream.readInt(); + byte blockid = stream.readByte(); + + Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); + tile.setBlock(Block.getByID(blockid)); + + if(tile.block() == Blocks.blockpart){ + tile.link = stream.readByte(); + } + + if(tile.entity != null){ + byte rotation = stream.readByte(); + short health = stream.readShort(); + + tile.entity.health = health; + tile.setRotation(rotation); + + for(int j = 0; j < tile.entity.items.length; j ++){ + tile.entity.items[j] = stream.readInt(); + } + + byte timers = stream.readByte(); + for(int time = 0; time < timers; time ++){ + tile.entity.timer.getTimes()[time] = stream.readFloat(); + } + + tile.entity.read(stream); + } + } + + }catch (IOException e){ + throw new RuntimeException(e); + } + } +} diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index 261226ea6f..d15770b443 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -1,28 +1,31 @@ package io.anuke.mindustry.io; -import static io.anuke.mindustry.Vars.android; - -import java.io.*; -import java.util.Arrays; -import java.util.Date; - -import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Base64Coder; import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.reflect.ClassReflection; - import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.enemies.*; import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.GameMode; +import io.anuke.mindustry.world.Map; +import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.ucore.core.Core; +import io.anuke.ucore.core.Settings; import io.anuke.ucore.entities.Entities; +import java.io.*; +import java.util.Arrays; +import java.util.Date; + +import static io.anuke.mindustry.Vars.android; + /* * Save format: * @@ -84,7 +87,7 @@ public class SaveIO{ private static final int fileVersionID = 12; //TODO automatic registration of types? - private static final Array> enemyIDs = Array.with( + public static final Array> enemyIDs = Array.with( Enemy.class, FastEnemy.class, RapidEnemy.class, @@ -98,78 +101,78 @@ public class SaveIO{ EmpEnemy.class ); - private static final ObjectMap, Byte> idEnemies = new ObjectMap, Byte>(){{ + public static final ObjectMap, Byte> idEnemies = new ObjectMap, Byte>(){{ for(int i = 0; i < enemyIDs.size; i ++){ put(enemyIDs.get(i), (byte)i); } }}; public static void saveToSlot(int slot){ - write(fileFor(slot)); + if(Vars.gwt){ + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + write(stream); + Settings.putString("save-"+slot+"-data", new String(Base64Coder.encode(stream.toByteArray()))); + Settings.save(); + }else{ + write(fileFor(slot)); + } } public static void loadFromSlot(int slot){ - load(fileFor(slot)); + if(Vars.gwt){ + String string = Settings.getString("save-"+slot+"-data"); + ByteArrayInputStream stream = new ByteArrayInputStream(Base64Coder.decode(string)); + load(stream); + }else{ + load(fileFor(slot)); + } + } + + public static DataInputStream readSlotMeta(int slot){ + if(Vars.gwt){ + String string = Settings.getString("save-"+slot+"-data"); + byte[] bytes = Base64Coder.decode(string); + return new DataInputStream(new ByteArrayInputStream(bytes)); + }else{ + return new DataInputStream(fileFor(slot).read()); + } } public static boolean isSaveValid(int slot){ - return isSaveValid(fileFor(slot)); - } - - public static boolean isSaveValid(FileHandle file){ - try(DataInputStream stream = new DataInputStream(file.read())){ - int version = stream.readInt(); //read version - stream.readLong(); //read last saved time - stream.readByte(); //read the gamemode - byte map = stream.readByte(); //read the map - return version == fileVersionID && Vars.world.maps().getMap(map) != null; + try { + return isSaveValid(readSlotMeta(slot)); }catch (Exception e){ return false; } } - - public static String getTimeString(int slot){ - - try(DataInputStream stream = new DataInputStream(fileFor(slot).read())){ - stream.readInt(); - Date date = new Date(stream.readLong()); - return Mindustry.platforms.format(date); - }catch (IOException e){ - throw new RuntimeException(e); + + public static boolean isSaveValid(FileHandle file){ + return isSaveValid(new DataInputStream(file.read())); + } + + public static boolean isSaveValid(DataInputStream stream){ + + try{ + SaveMeta meta = getData(stream); + return meta.version == fileVersionID && meta.map != null; + }catch (Exception e){ + return false; } } - - public static int getWave(int slot){ - - try(DataInputStream stream = new DataInputStream(fileFor(slot).read())){ - stream.readInt(); //read version - stream.readLong(); //read last saved time - stream.readByte(); //read the gamemode - stream.readByte(); //read the map - return stream.readInt(); //read the wave - }catch (IOException e){ - throw new RuntimeException(e); - } + + public static SaveMeta getData(int slot){ + return getData(readSlotMeta(slot)); } - public static GameMode getMode(int slot){ + public static SaveMeta getData(DataInputStream stream){ - try(DataInputStream stream = new DataInputStream(fileFor(slot).read())){ - stream.readInt(); //read version - stream.readLong(); //read last saved time - return GameMode.values()[stream.readByte()]; //read the gamemode - }catch (IOException e){ - throw new RuntimeException(e); - } - } - - public static Map getMap(int slot){ - - try(DataInputStream stream = new DataInputStream(fileFor(slot).read())){ - stream.readInt(); //read version - stream.readLong(); //read last saved time - stream.readByte(); //read the gamemode - return Vars.world.maps().getMap(stream.readByte()); //read the map + try{ + int version = stream.readInt(); //read version + long time = stream.readLong(); //read last saved time + byte mode = stream.readByte(); //read the gamemode + byte map = stream.readByte(); //read the map + int wave = stream.readInt(); //read the wave + return new SaveMeta(version, time, mode, map, wave); }catch (IOException e){ throw new RuntimeException(e); } @@ -245,7 +248,7 @@ public class SaveIO{ for(Enemy enemy : Vars.control.enemyGroup.all()){ if(idEnemies.containsKey(enemy.getClass())){ stream.writeByte(idEnemies.get(enemy.getClass())); //type - stream.writeByte(enemy.spawn); //lane + stream.writeByte(enemy.lane); //lane stream.writeFloat(enemy.x); //x stream.writeFloat(enemy.y); //y stream.writeByte(enemy.tier); //tier @@ -391,7 +394,7 @@ public class SaveIO{ try{ Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); - enemy.spawn = lane; + enemy.lane = lane; enemy.health = health; enemy.x = x; enemy.y = y; diff --git a/core/src/io/anuke/mindustry/io/SaveMeta.java b/core/src/io/anuke/mindustry/io/SaveMeta.java new file mode 100644 index 0000000000..1c2d03710c --- /dev/null +++ b/core/src/io/anuke/mindustry/io/SaveMeta.java @@ -0,0 +1,24 @@ +package io.anuke.mindustry.io; + +import io.anuke.mindustry.Mindustry; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.world.GameMode; +import io.anuke.mindustry.world.Map; + +import java.util.Date; + +public class SaveMeta { + public int version; + public String date; + public GameMode mode; + public Map map; + public int wave; + + public SaveMeta(int version, long date, int mode, int map, int wave){ + this.version = version; + this.date = Mindustry.platforms.format(new Date(date)); + this.mode = GameMode.values()[mode]; + this.map = Vars.world.maps().getMap(map); + this.wave = wave; + } +} diff --git a/core/src/io/anuke/mindustry/io/Saves.java b/core/src/io/anuke/mindustry/io/Saves.java index 140034b6e3..c1bd4e7cfc 100644 --- a/core/src/io/anuke/mindustry/io/Saves.java +++ b/core/src/io/anuke/mindustry/io/Saves.java @@ -2,12 +2,7 @@ package io.anuke.mindustry.io; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Timer; -import com.badlogic.gdx.utils.Timer.Task; import com.badlogic.gdx.utils.async.AsyncExecutor; -import com.badlogic.gdx.utils.reflect.ClassReflection; -import com.badlogic.gdx.utils.reflect.Field; -import com.badlogic.gdx.utils.reflect.ReflectionException; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState.State; @@ -31,7 +26,9 @@ public class Saves { saves.clear(); for(int i = 0; i < Vars.saveSlots; i ++){ if(SaveIO.isSaveValid(i)){ - saves.add(new SaveSlot(i)); + SaveSlot slot = new SaveSlot(i); + saves.add(slot); + slot.meta = SaveIO.getData(i); nextSlot = i + 1; } } @@ -78,6 +75,7 @@ public class Saves { slot.setName(name); saves.add(slot); SaveIO.saveToSlot(slot.index); + slot.meta = SaveIO.getData(slot.index); } public Array getSaveSlots(){ @@ -86,6 +84,7 @@ public class Saves { public class SaveSlot{ public final int index; + SaveMeta meta; public SaveSlot(int index){ this.index = index; @@ -94,19 +93,21 @@ public class Saves { public void load(){ current = this; SaveIO.loadFromSlot(index); + meta = SaveIO.getData(index); } public void save(){ current = this; SaveIO.saveToSlot(index); + meta = SaveIO.getData(index); } public String getDate(){ - return SaveIO.getTimeString(index); + return meta.date; } public Map getMap(){ - return SaveIO.getMap(index); + return meta.map; } public String getName(){ @@ -119,11 +120,11 @@ public class Saves { } public int getWave(){ - return SaveIO.getWave(index); + return meta.wave; } public GameMode getMode(){ - return SaveIO.getMode(index); + return meta.mode; } public boolean isAutosave(){ @@ -156,6 +157,7 @@ public class Saves { public void delete(){ SaveIO.fileFor(index).delete(); + saves.removeValue(this, true); if(this == current){ current = null; } diff --git a/core/src/io/anuke/mindustry/net/Address.java b/core/src/io/anuke/mindustry/net/Address.java new file mode 100644 index 0000000000..57b20f1e16 --- /dev/null +++ b/core/src/io/anuke/mindustry/net/Address.java @@ -0,0 +1,11 @@ +package io.anuke.mindustry.net; + +public class Address { + public final String name; + public final String address; + + public Address(String name, String address){ + this.name = name; + this.address = address; + } +} diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index af0d55c8c2..c63addab63 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -1,8 +1,11 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.ObjectMap; +import com.badlogic.gdx.utils.async.AsyncExecutor; import io.anuke.mindustry.net.Streamable.StreamBegin; import io.anuke.mindustry.net.Streamable.StreamBuilder; import io.anuke.mindustry.net.Streamable.StreamChunk; @@ -21,6 +24,7 @@ public class Net{ private static int lastConnection = -1; private static IntMap streams = new IntMap<>(); + private static AsyncExecutor executor = new AsyncExecutor(4); /**Connect to an address.*/ public static void connect(String ip, int port) throws IOException{ @@ -48,6 +52,23 @@ public class Net{ server = false; active = false; } + + /**Starts discovering servers on a different thread. Does not work with GWT. + * Callback is run on the main libGDX thread.*/ + public static void discoverServers(Consumer> cons){ + executor.submit(() -> { + Array
arr = clientProvider.discover(); + Gdx.app.postRunnable(() -> { + cons.accept(arr); + }); + return false; + }); + } + + /**Returns a list of all connections IDs.*/ + public static IntArray getConnections(){ + return serverProvider.getConnections(); + } /**Send an object to all connected clients, or to the server if this is a client.*/ public static void send(Object object, SendMode mode){ @@ -146,6 +167,12 @@ public class Net{ return !server; } + public static void dispose(){ + clientProvider.dispose(); + serverProvider.dispose(); + executor.dispose(); + } + /**Register classes that will be sent. Must be done for all classes.*/ public static void registerClasses(Class... classes){ clientProvider.register(classes); @@ -164,8 +191,12 @@ public class Net{ public int getPing(); /**Disconnect from the server.*/ public void disconnect(); + /**Discover servers. This should block for a certain amount of time, and will most likely be run in a different thread.*/ + public Array
discover(); /**Register classes to be sent.*/ public void register(Class... types); + /**Close all connections.*/ + public void dispose(); } /**Server implementation.*/ @@ -182,8 +213,14 @@ public class Net{ public void sendExcept(int id, Object object, SendMode mode); /**Close the server connection.*/ public void close(); + /**Return all connected users.*/ + public IntArray getConnections(); /**Register classes to be sent.*/ public void register(Class... types); + /**Returns the ping for a certain connection.*/ + public int getPingFor(int connection); + /**Close all connections.*/ + public void dispose(); } public enum SendMode{ diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 716cac8ed5..965ae42a9f 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.net; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.enemies.Enemy; /**Class for storing all packets.*/ public class Packets { @@ -27,16 +28,27 @@ public class Packets { public static class SyncPacket{ public int[] ids; public float[][] data; + public short enemyStart; } public static class BlockSyncPacket extends Streamable{ } - public static class StateSyncPacket { + public static class ConnectPacket{ + public String name; + public boolean android; + } + + public static class DisconnectPacket{ + public int playerid; + } + + public static class StateSyncPacket{ public int[] items; - public float countdown; + public float countdown, time; public int enemies, wave; + public long timestamp; } public static class PositionPacket{ @@ -55,6 +67,12 @@ public class Packets { public int playerid; } + public static class BulletPacket{ + public int type, owner; + public float x, y, angle; + public short damage; + } + public static class PlacePacket{ public int playerid; public byte rotation; @@ -66,4 +84,28 @@ public class Packets { public int playerid; public short x, y; } + + public static class EnemySpawnPacket{ + public Class type; + public byte lane, tier; + public float x, y; + public int id; + } + + public static class EnemyDeathPacket{ + public int id; + } + + public static class PathPacket{ + public int[] path; + public byte index; + } + + public static class BlockDestroyPacket{ + public int position; + } + + public static class BlockUpdatePacket{ + public int health, position; + } } diff --git a/core/src/io/anuke/mindustry/net/Registrator.java b/core/src/io/anuke/mindustry/net/Registrator.java index 3555e95854..15537a7ada 100644 --- a/core/src/io/anuke/mindustry/net/Registrator.java +++ b/core/src/io/anuke/mindustry/net/Registrator.java @@ -3,6 +3,7 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.enemies.*; import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Streamable.StreamBegin; import io.anuke.mindustry.net.Streamable.StreamChunk; @@ -24,12 +25,21 @@ public class Registrator { BreakPacket.class, StateSyncPacket.class, BlockSyncPacket.class, + EnemySpawnPacket.class, + PathPacket.class, + BulletPacket.class, + EnemyDeathPacket.class, + BlockUpdatePacket.class, + BlockDestroyPacket.class, + ConnectPacket.class, + DisconnectPacket.class, Class.class, byte[].class, float[].class, float[][].class, int[].class, + int[][].class, Entity[].class, Player[].class, Array.class, @@ -37,7 +47,19 @@ public class Registrator { Entity.class, Player.class, - Mech.class + Mech.class, + + Enemy.class, + FastEnemy.class, + RapidEnemy.class, + FlamerEnemy.class, + TankEnemy.class, + BlastEnemy.class, + MortarEnemy.class, + TestEnemy.class, + HealerEnemy.class, + TitanEnemy.class, + EmpEnemy.class }; } } diff --git a/core/src/io/anuke/mindustry/net/Syncable.java b/core/src/io/anuke/mindustry/net/Syncable.java index 6a31fa5e93..a5fee2b06b 100644 --- a/core/src/io/anuke/mindustry/net/Syncable.java +++ b/core/src/io/anuke/mindustry/net/Syncable.java @@ -2,9 +2,15 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.math.Vector2; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.graphics.Fx; +import io.anuke.ucore.core.Effects; +import io.anuke.ucore.core.Timers; import io.anuke.ucore.entities.Entity; +import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Mathf; +//TODO clean up this giant mess public interface Syncable { public Interpolator getInterpolator(); @@ -29,6 +35,37 @@ public interface Syncable { @Override public void update(Player entity, Interpolator interpolator) { + Interpolator i = entity.getInterpolator(); + if(i.target.dst(entity.x, entity.y) > 16 && !entity.isAndroid){ + entity.set(i.target.x, i.target.y); + } + + if(entity.isAndroid && i.target.dst(entity.x, entity.y) > 2f && Timers.get(entity, "dashfx", 2)){ + Angles.translation(entity.angle + 180, 3f); + Effects.effect(Fx.dashsmoke, entity.x + Angles.x(), entity.y + Angles.y()); + } + + entity.x = Mathf.lerpDelta(entity.x, i.target.x, 0.4f); + entity.y = Mathf.lerpDelta(entity.y, i.target.y, 0.4f); + entity.angle = Mathf.lerpAngDelta(entity.angle, i.targetrot, 0.6f); + } + }; + + public static final SyncType enemy = new SyncType() { + @Override + public float[] write(Enemy entity) { + return new float[]{entity.x, entity.y, entity.angle, entity.health}; + } + + @Override + public void read(Enemy entity, float[] data) { + entity.getInterpolator().target.set(data[0], data[1]); + entity.getInterpolator().targetrot = data[2]; + entity.health = (int)data[3]; + } + + @Override + public void update(Enemy entity, Interpolator interpolator) { Interpolator i = entity.getInterpolator(); if(i.target.dst(entity.x, entity.y) > 16){ entity.set(i.target.x, i.target.y); @@ -44,6 +81,7 @@ public interface Syncable { public static class Interpolator { public SyncType type; public Vector2 target = new Vector2(); + public Vector2 last = new Vector2(); public float targetrot; public Interpolator(SyncType type){ diff --git a/core/src/io/anuke/mindustry/resource/Item.java b/core/src/io/anuke/mindustry/resource/Item.java index d5ea87d2b1..4b1d81c249 100644 --- a/core/src/io/anuke/mindustry/resource/Item.java +++ b/core/src/io/anuke/mindustry/resource/Item.java @@ -13,10 +13,10 @@ public class Item{ steel = new Item("steel"), titanium = new Item("titanium"), dirium = new Item("dirium"), - uranium = new Item("uranium"), - sand = new Item("sand"), + uranium = new Item("uranium"); + /*sand = new Item("sand"), glass = new Item("glass"), - silicon = new Item("silicon"); + silicon = new Item("silicon");*/ public final int id; public final String name; diff --git a/core/src/io/anuke/mindustry/ui/JoinDialog.java b/core/src/io/anuke/mindustry/ui/JoinDialog.java new file mode 100644 index 0000000000..f3d34b94df --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/JoinDialog.java @@ -0,0 +1,103 @@ +package io.anuke.mindustry.ui; + +import com.badlogic.gdx.utils.Array; +import io.anuke.mindustry.Mindustry; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.net.Address; +import io.anuke.mindustry.net.Net; +import io.anuke.ucore.core.Settings; +import io.anuke.ucore.core.Timers; +import io.anuke.ucore.scene.style.Drawable; +import io.anuke.ucore.scene.ui.Dialog; +import io.anuke.ucore.scene.ui.TextButton; +import io.anuke.ucore.scene.ui.TextField.TextFieldFilter.DigitsOnlyFilter; +import io.anuke.ucore.scene.ui.layout.Table; +import io.anuke.ucore.util.Bundles; +import io.anuke.ucore.util.Strings; + +import java.io.IOException; + +public class JoinDialog extends FloatingDialog { + Dialog join; + Table hosts = new Table(); + float w = 400; + + public JoinDialog(){ + super("$text.joingame"); + + addCloseButton(); + + join = new FloatingDialog("$text.joingame.title"); + join.content().add("$text.joingame.ip").left(); + Mindustry.platforms.addDialog(join.content().addField(Settings.getString("ip"),text ->{ + Settings.putString("ip", text); + Settings.save(); + }).size(180f, 54f).get()); + + join.content().row(); + join.content().add("$text.server.port").left(); + Mindustry.platforms.addDialog(join.content() + .addField(Settings.getString("port"), new DigitsOnlyFilter(), text ->{ + Settings.putString("port", text); + Settings.save(); + }) + .size(180f, 54f).get()); + join.buttons().defaults().size(140f, 60f).pad(4f); + join.buttons().addButton("$text.cancel", join::hide); + join.buttons().addButton("$text.ok", () -> + connect(Settings.getString("port"), Integer.parseInt(Settings.getString("port"))) + ).disabled(b -> Settings.getString("ip").isEmpty() || Integer.parseInt(Settings.getString("port")) == Integer.MIN_VALUE); + + setup(); + + shown(() -> { + hosts.clear(); + hosts.background("button"); + hosts.label(() -> "[accent]" + Bundles.get("text.hosts.discovering") + new String(new char[(int)(Timers.time() / 10) % 4]).replace("\0", ".")).pad(10f); + Net.discoverServers(list -> { + addHosts(list); + }); + }); + } + + void setup(){ + hosts.background("button"); + content().clear(); + content().add(hosts).width(w).pad(0); + content().row(); + content().addButton("$text.joingame.byip", "clear", join::show).width(w).height(80f); + } + + void addHosts(Array
array){ + hosts.clear(); + + if(array.size == 0){ + hosts.add("$text.hosts.none").pad(20f); + }else { + for (Address a : array) { + TextButton button = hosts.addButton("[accent]"+a.name, "clear", () -> { + connect(a.address, Vars.port); + }).width(w).height(80f).pad(4f).get(); + button.left(); + button.row(); + button.add("[lightgray]" + a.address + " / " + Vars.port).pad(4).left(); + + hosts.row(); + hosts.background((Drawable) null); + } + } + } + + void connect(String ip, int port){ + Vars.ui.showLoading("$text.connecting"); + + Timers.runTask(2f, () -> { + try{ + Net.connect(ip, port); + }catch (IOException e) { + Vars.ui.showError(Bundles.format("text.connectfail", Strings.parseException(e, false))); + Vars.ui.hideLoading(); + } + }); + } +} diff --git a/core/src/io/anuke/mindustry/ui/MenuDialog.java b/core/src/io/anuke/mindustry/ui/MenuDialog.java index dd484104a5..9642a55741 100644 --- a/core/src/io/anuke/mindustry/ui/MenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/MenuDialog.java @@ -1,23 +1,18 @@ package io.anuke.mindustry.ui; -import static io.anuke.mindustry.Vars.ui; - import com.badlogic.gdx.utils.reflect.ClassReflection; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.net.Net; -import io.anuke.ucore.UCore; import io.anuke.ucore.core.Timers; import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.builders.build; import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.ui.ImageButton; -import io.anuke.ucore.scene.ui.TextField.TextFieldFilter.DigitsOnlyFilter; import io.anuke.ucore.util.Bundles; -import io.anuke.ucore.util.Strings; -import java.io.IOException; +import static io.anuke.mindustry.Vars.ui; public class MenuDialog extends FloatingDialog{ private SaveDialog save = new SaveDialog(); @@ -25,14 +20,14 @@ public class MenuDialog extends FloatingDialog{ public boolean wasPaused = false; public MenuDialog() { - super("Paused"); + super("$text.menu"); setup(); } void setup(){ shown(() -> { wasPaused = GameState.is(State.paused); - GameState.set(State.paused); + if(!Net.active()) GameState.set(State.paused); }); if(!Vars.android){ @@ -40,7 +35,7 @@ public class MenuDialog extends FloatingDialog{ content().addButton("$text.back", () -> { hide(); - if(!wasPaused) + if(!wasPaused || Net.active()) GameState.set(State.playing); }); @@ -49,39 +44,25 @@ public class MenuDialog extends FloatingDialog{ ui.showPrefs(); }); - if(!Vars.gwt){ - content().row(); - content().addButton("$text.savegame", () -> { - save.show(); - }); + content().row(); + content().addButton("$text.savegame", () -> { + save.show(); + }); - content().row(); - content().addButton("$text.loadgame", () -> { - load.show(); - }); - } + content().row(); + content().addButton("$text.loadgame", () -> { + load.show(); + }); content().row(); - content().addButton("$text.hostserver", () -> { - Vars.ui.showTextInput("$text.hostserver", "$text.server.port", Vars.port + "", new DigitsOnlyFilter(), text -> { - int result = Strings.parseInt(text); - if(result == Integer.MIN_VALUE || result >= 65535){ - Vars.ui.showError("$text.server.invalidport"); - }else{ - try{ - Net.host(result); - }catch (IOException e){ - Vars.ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false))); - } - } - }); - }).disabled(b -> Net.active() || (Net.active() && !Net.server())); + content().addButton("$text.hostserver", () -> ui.showHostServer()) + .disabled(b -> Net.active() || (Net.active() && !Net.server())); content().row(); content().addButton("$text.quit", () -> { - Vars.ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> { + ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> { runSave(); hide(); GameState.set(State.menu); @@ -105,8 +86,12 @@ public class MenuDialog extends FloatingDialog{ new imagebutton("icon-tools", isize, () -> ui.showPrefs()).text("$text.settings").padTop(4f); new imagebutton("icon-save", isize, ()-> save.show()).text("$text.save").padTop(4f); + + content().row(); new imagebutton("icon-load", isize, () -> load.show()).text("$text.load").padTop(4f); + + new imagebutton("icon-host", isize, () -> ui.showHostServer()).text("$text.host").padTop(4f); new imagebutton("icon-quit", isize, () -> { Vars.ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> { diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index 3b655feac0..8cf72e324f 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -1,34 +1,26 @@ package io.anuke.mindustry.ui.fragments; -import static io.anuke.mindustry.Vars.*; - -import com.badlogic.gdx.Game; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; - import com.badlogic.gdx.math.Interpolation; -import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.world.GameMode; -import io.anuke.ucore.UCore; +import io.anuke.mindustry.net.Net; import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Draw; import io.anuke.ucore.core.Settings; import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.table; import io.anuke.ucore.scene.event.Touchable; -import io.anuke.ucore.scene.ui.Image; import io.anuke.ucore.scene.ui.ImageButton; import io.anuke.ucore.scene.ui.Label; -import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.util.Bundles; +import static io.anuke.mindustry.Vars.*; + public class HudFragment implements Fragment{ private ImageButton menu, flip, pause; private Table respawntable; @@ -74,7 +66,8 @@ public class HudFragment implements Fragment{ pause = new imagebutton("icon-pause", isize, ()->{ GameState.set(GameState.is(State.paused) ? State.playing : State.paused); - }).update(i -> i.getStyle().imageUp = Core.skin.getDrawable(GameState.is(State.paused) ? "icon-play" : "icon-pause")).get(); + }).update(i -> i.getStyle().imageUp = Core.skin.getDrawable(GameState.is(State.paused) ? "icon-play" : "icon-pause")).cell + .disabled(b -> Net.active()).get(); }}.end(); @@ -105,7 +98,7 @@ public class HudFragment implements Fragment{ //paused table new table(){{ - visible(()->GameState.is(State.paused)); + visible(()->GameState.is(State.paused) && !Net.active()); atop(); new table("pane"){{ @@ -157,6 +150,14 @@ public class HudFragment implements Fragment{ }}.end(); + if(Vars.debugNet) { + new table() {{ + new label(() -> "players: " + Vars.control.playerGroup.amount()); + row(); + new label(() -> "" + Vars.control.playerGroup.all()); + }}.end(); + } + blockfrag.build(); } diff --git a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java index a7aeb3706e..e3df7132c0 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java @@ -1,9 +1,6 @@ package io.anuke.mindustry.ui.fragments; -import static io.anuke.mindustry.Vars.*; - import com.badlogic.gdx.Gdx; - import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState.State; @@ -12,6 +9,8 @@ import io.anuke.mindustry.ui.PressGroup; import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.table; +import static io.anuke.mindustry.Vars.*; + public class MenuFragment implements Fragment{ public void build(){ @@ -35,11 +34,11 @@ public class MenuFragment implements Fragment{ add(new MenuButton("$text.tutorial", group, ()-> control.playMap(world.maps().getMap("tutorial")))); row(); - + + add(new MenuButton("$text.loadgame", group, ui::showLoadGame)); + row(); + if(!gwt){ - add(new MenuButton("$text.loadgame", group, ui::showLoadGame)); - row(); - add(new MenuButton("$text.editor", group, ui::showEditor)); row(); } @@ -72,6 +71,8 @@ public class MenuFragment implements Fragment{ new imagebutton("icon-editor", isize, () -> ui.showEditor()).text("$text.editor").padTop(4f); new imagebutton("icon-tools", isize, () -> ui.showPrefs()).text("$text.settings").padTop(4f); + + new imagebutton("icon-add", isize, () -> ui.showJoinGame()).text("$text.joingame").padTop(4f); if(Mindustry.donationsCallable != null){ new imagebutton("icon-donate", isize, () -> { diff --git a/core/src/io/anuke/mindustry/world/Generator.java b/core/src/io/anuke/mindustry/world/Generator.java index 66444db661..693e4008d1 100644 --- a/core/src/io/anuke/mindustry/world/Generator.java +++ b/core/src/io/anuke/mindustry/world/Generator.java @@ -17,7 +17,7 @@ import io.anuke.ucore.noise.Noise; import io.anuke.ucore.util.Mathf; public class Generator{ - static final ObjectMap rocks = new ObjectMap(){{ + public static final ObjectMap rocks = new ObjectMap(){{ put(Blocks.stone, Blocks.rock); put(Blocks.snow, Blocks.icerock); put(Blocks.grass, Blocks.shrub); diff --git a/core/src/io/anuke/mindustry/world/SpawnPoint.java b/core/src/io/anuke/mindustry/world/SpawnPoint.java index 94a0293203..9735946ecd 100644 --- a/core/src/io/anuke/mindustry/world/SpawnPoint.java +++ b/core/src/io/anuke/mindustry/world/SpawnPoint.java @@ -8,7 +8,6 @@ import io.anuke.mindustry.ai.SmoothGraphPath; public class SpawnPoint{ public Tile start; public Tile[] pathTiles; - public Tile[] tempTiles; public PathFinder finder; public SmoothGraphPath path = new SmoothGraphPath(); public PathFinderRequest request; diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index 8fc45c73a7..52db18ee55 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -1,16 +1,15 @@ package io.anuke.mindustry.world; -import static io.anuke.mindustry.Vars.tilesize; - import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; - import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.Mathf; +import static io.anuke.mindustry.Vars.tilesize; + public class Tile{ private static final Array tmpArray = new Array<>(); @@ -34,6 +33,10 @@ public class Tile{ this(x, y); iSetFloor(floor); } + + public int packedPosition(){ + return x + y * Vars.world.width(); + } private void iSetFloor(Block floor){ byte id = (byte)floor.id; diff --git a/core/src/io/anuke/mindustry/world/blocks/Blocks.java b/core/src/io/anuke/mindustry/world/blocks/Blocks.java index f8137dbde9..4e66ed2346 100644 --- a/core/src/io/anuke/mindustry/world/blocks/Blocks.java +++ b/core/src/io/anuke/mindustry/world/blocks/Blocks.java @@ -138,34 +138,23 @@ public class Blocks{ solid = true; }}, - shrub = new Block("shrub"){{ - shadow = "shrubshadow"; - breakable = true; - breaktime = 10; - }}, + shrub = new Rock("shrub"){ + + }, - rock = new Block("rock"){{ - shadow = "rockshadow"; - breakable = true; - breaktime = 15; + rock = new Rock("rock"){{ variants = 2; varyShadow = true; drops = new ItemStack(Item.stone, 3); }}, - icerock = new Block("icerock"){{ - shadow = "rockshadow"; - breakable = true; - breaktime = 15; + icerock = new Rock("icerock"){{ variants = 2; varyShadow = true; drops = new ItemStack(Item.stone, 3); }}, - blackrock = new Block("blackrock"){{ - shadow = "blackrockshadow"; - breakable = true; - breaktime = 15; + blackrock = new Rock("blackrock"){{ variants = 1; varyShadow = true; drops = new ItemStack(Item.stone, 3); diff --git a/core/src/io/anuke/mindustry/world/blocks/types/Rock.java b/core/src/io/anuke/mindustry/world/blocks/types/Rock.java new file mode 100644 index 0000000000..14a8681c3a --- /dev/null +++ b/core/src/io/anuke/mindustry/world/blocks/types/Rock.java @@ -0,0 +1,13 @@ +package io.anuke.mindustry.world.blocks.types; + +import io.anuke.mindustry.world.Block; + +public class Rock extends Block { + + public Rock(String name) { + super(name); + shadow = name+"shadow"; + breakable = true; + breaktime = 10; + } +} diff --git a/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java b/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java index 47cd9b0830..89e9def39e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java @@ -1,15 +1,7 @@ package io.anuke.mindustry.world.blocks.types.distribution; -import static io.anuke.mindustry.Vars.tilesize; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.*; - import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.IntArray; - import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.world.Block; @@ -17,7 +9,20 @@ import io.anuke.mindustry.world.Layer; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.core.Draw; import io.anuke.ucore.core.Timers; -import io.anuke.ucore.util.*; +import io.anuke.ucore.util.Bits; +import io.anuke.ucore.util.Mathf; +import io.anuke.ucore.util.Strings; +import io.anuke.ucore.util.Tmp; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.AbstractList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import static io.anuke.mindustry.Vars.tilesize; public class Conveyor extends Block{ private static ItemPos pos1 = new ItemPos(); @@ -182,6 +187,7 @@ public class Conveyor extends Block{ @Override public void read(DataInputStream stream) throws IOException{ + convey.clear(); int amount = stream.readInt(); convey.ensureCapacity(amount); diff --git a/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java b/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java index ed34a4e14b..4d4da9bae5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java @@ -1,15 +1,14 @@ package io.anuke.mindustry.world.blocks.types.production; -import java.util.Arrays; - import com.badlogic.gdx.utils.Array; - import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.core.Effects; +import java.util.Arrays; + public class Crafter extends Block{ protected final int timerDump = timers++; @@ -32,7 +31,7 @@ public class Crafter extends Block{ @Override public void update(Tile tile){ - if(tile.entity.timer.get(timerDump, 20) && tile.entity.hasItem(result)){ + if(tile.entity.timer.get(timerDump, 15) && tile.entity.hasItem(result)){ tryDump(tile, -1, result); } @@ -52,13 +51,11 @@ public class Crafter extends Block{ @Override public boolean acceptItem(Item item, Tile dest, Tile source){ - boolean craft = false; for(Item req : requirements){ if(item == req){ - craft = true; - break; + return true; } } - return craft; + return false; } } diff --git a/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java b/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java index 6b7fde431b..2b82636b58 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java @@ -75,7 +75,7 @@ public class LiquidCrafter extends LiquidBlock{ Effects.effect(craftEffect, tile.worldx(), tile.worldy()); } - if(entity.timer.get(timerDump, 30)){ + if(entity.timer.get(timerDump, 15)){ tryDump(tile, -1, output); } } diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index f160b06ba5..a35cfa3ec0 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -6,32 +6,25 @@ import club.minnced.discord.rpc.DiscordRichPresence; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; import com.badlogic.gdx.utils.Array; -import com.esotericsoftware.kryonet.*; -import com.esotericsoftware.kryonet.util.InputStreamSender; -import com.esotericsoftware.minlog.Log; +import io.anuke.kryonet.KryoClient; +import io.anuke.kryonet.KryoServer; import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Vars; import io.anuke.mindustry.io.PlatformFunction; import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.Net.ClientProvider; -import io.anuke.mindustry.net.Net.SendMode; -import io.anuke.mindustry.net.Net.ServerProvider; -import io.anuke.mindustry.net.Packets.Connect; -import io.anuke.mindustry.net.Packets.Disconnect; -import io.anuke.mindustry.net.Registrator; -import io.anuke.mindustry.net.Streamable; -import io.anuke.mindustry.net.Streamable.StreamBegin; -import io.anuke.mindustry.net.Streamable.StreamChunk; -import io.anuke.ucore.UCore; import io.anuke.ucore.scene.ui.TextField; +import io.anuke.ucore.util.Strings; +import javax.swing.*; import java.awt.*; +import java.io.File; import java.io.IOException; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.Date; public class DesktopLauncher { @@ -74,219 +67,57 @@ public class DesktopLauncher { @Override public void onSceneChange(String state, String details, String icon) { - DiscordRPC lib = DiscordRPC.INSTANCE; + DiscordRPC lib = DiscordRPC.INSTANCE; - String applicationId = "397335883319083018"; + String applicationId = "397335883319083018"; - DiscordEventHandlers handlers = new DiscordEventHandlers(); + DiscordEventHandlers handlers = new DiscordEventHandlers(); - lib.Discord_Initialize(applicationId, handlers, true, ""); + lib.Discord_Initialize(applicationId, handlers, true, ""); - DiscordRichPresence presence = new DiscordRichPresence(); - presence.startTimestamp = System.currentTimeMillis() / 1000; // epoch second - presence.state = state; - //presence.details = details; - presence.largeImageKey = "logo"; - presence.largeImageText = details; - lib.Discord_UpdatePresence(presence); - } + DiscordRichPresence presence = new DiscordRichPresence(); + presence.startTimestamp = System.currentTimeMillis() / 1000; // epoch second + presence.state = state; + //presence.details = details; + presence.largeImageKey = "logo"; + presence.largeImageText = details; + lib.Discord_UpdatePresence(presence); + } - @Override - public void onGameExit() { - DiscordRPC.INSTANCE.Discord_Shutdown(); - } - }; + @Override + public void onGameExit() { + DiscordRPC.INSTANCE.Discord_Shutdown(); + } + }; Mindustry.args = Array.with(arg); - Log.set(Log.LEVEL_DEBUG); + Net.setClientProvider(new KryoClient()); + Net.setServerProvider(new KryoServer()); - Net.setClientProvider(new ClientProvider() { - Client client; + try { + new Lwjgl3Application(new Mindustry(), config); + }catch (Exception e){ + e.printStackTrace(); - { - client = new Client(); - client.start(); - client.addListener(new Listener(){ - @Override - public void connected (Connection connection) { - Connect c = new Connect(); - c.id = connection.getID(); - c.addressTCP = connection.getRemoteAddressTCP().toString(); - Net.handleClientReceived(c); - } + //don't create crash logs for me, as it's expected + if(System.getProperty("user.name").equals("anuke")) return; - @Override - public void disconnected (Connection connection) { - Disconnect c = new Disconnect(); - Net.handleClientReceived(c); - } + String result = Strings.parseException(e, true); + boolean failed = false; - @Override - public void received (Connection connection, Object object) { - if(object instanceof FrameworkMessage) return; - Net.handleClientReceived(object); - } - }); + String filename = "crash-report-" + DateFormat.getDateTimeInstance().format(new Date()) + ".txt"; - register(Registrator.getClasses()); - } + try{ + Files.write(Paths.get(filename), result.getBytes()); + }catch (IOException i){ + i.printStackTrace(); + failed = true; + } - @Override - public void connect(String ip, int port) throws IOException { - client.connect(5000, ip, port, port); - } - - @Override - public void disconnect() { - client.close(); - } - - @Override - public void send(Object object, SendMode mode) { - if(mode == SendMode.tcp){ - client.sendTCP(object); - }else{ - client.sendUDP(object); - } - } - - @Override - public void updatePing() { - client.updateReturnTripTime(); - } - - @Override - public int getPing() { - return client.getReturnTripTime(); - } - - @Override - public void register(Class... types) { - for(Class c : types){ - client.getKryo().register(c); - } - } - }); - - Net.setServerProvider(new ServerProvider() { - Server server; - - { - server = new Server(); - Thread thread = new Thread(server, "Kryonet Server"); - thread.setDaemon(true); - thread.start(); - server.addListener(new Listener(){ - @Override - public void connected (Connection connection) { - Connect c = new Connect(); - c.id = connection.getID(); - c.addressTCP = connection.getRemoteAddressTCP().toString(); - Net.handleServerReceived(c, c.id); - } - - @Override - public void disconnected (Connection connection) { - Disconnect c = new Disconnect(); - c.id = connection.getID(); - Net.handleServerReceived(c, c.id); - } - - @Override - public void received (Connection connection, Object object) { - if(object instanceof FrameworkMessage) return; - Net.handleServerReceived(object, connection.getID()); - } - }); - - register(Registrator.getClasses()); - } - - @Override - public void host(int port) throws IOException { - server.bind(port, port); - } - - @Override - public void close() { - server.close(); - } - - @Override - public void sendStream(int id, Streamable stream) { - Connection connection = getByID(id); - - connection.addListener(new InputStreamSender(stream.stream, 512) { - int id; - - protected void start () { - //send an object so the receiving side knows how to handle the following chunks - StreamBegin begin = new StreamBegin(); - begin.total = stream.stream.available(); - begin.type = stream.getClass(); - connection.sendTCP(begin); - id = begin.id; - UCore.log("Sending begin packet: " + begin); - } - - protected Object next (byte[] bytes) { - StreamChunk chunk = new StreamChunk(); - chunk.id = id; - chunk.data = bytes; - UCore.log("Sending chunk of size " + chunk.data.length); - return chunk; //wrap the byte[] with an object so the receiving side knows how to handle it. - } - }); - } - - @Override - public void send(Object object, SendMode mode) { - if(mode == SendMode.tcp){ - server.sendToAllTCP(object); - }else{ - server.sendToAllUDP(object); - } - } - - @Override - public void sendTo(int id, Object object, SendMode mode) { - if(mode == SendMode.tcp){ - server.sendToTCP(id, object); - }else{ - server.sendToUDP(id, object); - } - } - - @Override - public void sendExcept(int id, Object object, SendMode mode) { - if(mode == SendMode.tcp){ - server.sendToAllExceptTCP(id, object); - }else{ - server.sendToAllExceptUDP(id, object); - } - } - - @Override - public void register(Class... types) { - for(Class c : types){ - server.getKryo().register(c); - } - } - - Connection getByID(int id){ - for(Connection con : server.getConnections()){ - if(con.getID() == id){ - return con; - } - } - - throw new RuntimeException("Unable to find connection with ID " + id + "! Current connections: " - + Arrays.toString(server.getConnections())); - } - - }); - - new Lwjgl3Application(new Mindustry(), config); + JOptionPane.showMessageDialog(null, "An error has occured: \n" + result + "\n\n" + + (!failed ? "A crash report has been written to " + new File(filename).getAbsolutePath() + ".\nPlease send this file to the developer!" + : "Failed to generate crash report.\nPlease send an image of this crash log to the developer!")); + } } } diff --git a/kryonet/build.gradle b/kryonet/build.gradle new file mode 100644 index 0000000000..afd5e6ab73 --- /dev/null +++ b/kryonet/build.gradle @@ -0,0 +1,4 @@ +apply plugin: "java" + +sourceCompatibility = 1.8 +sourceSets.main.java.srcDirs = [ "src/" ] diff --git a/kryonet/build/libs/kryonet-release.jar b/kryonet/build/libs/kryonet-release.jar new file mode 100644 index 0000000000..a669518e69 Binary files /dev/null and b/kryonet/build/libs/kryonet-release.jar differ diff --git a/kryonet/build/tmp/jar/MANIFEST.MF b/kryonet/build/tmp/jar/MANIFEST.MF new file mode 100644 index 0000000000..59499bce4a --- /dev/null +++ b/kryonet/build/tmp/jar/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 + diff --git a/kryonet/src/io/anuke/kryonet/KryoClient.java b/kryonet/src/io/anuke/kryonet/KryoClient.java new file mode 100644 index 0000000000..8553b3c5d5 --- /dev/null +++ b/kryonet/src/io/anuke/kryonet/KryoClient.java @@ -0,0 +1,133 @@ +package io.anuke.kryonet; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.ObjectSet; +import com.esotericsoftware.kryonet.Client; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.FrameworkMessage; +import com.esotericsoftware.kryonet.Listener; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.net.Address; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.net.Net.ClientProvider; +import io.anuke.mindustry.net.Net.SendMode; +import io.anuke.mindustry.net.Packets.Connect; +import io.anuke.mindustry.net.Packets.Disconnect; +import io.anuke.mindustry.net.Registrator; + +import java.io.IOException; +import java.net.InetAddress; +import java.util.List; + +public class KryoClient implements ClientProvider{ + Client client; + + public KryoClient(){ + client = new Client(); + client.start(); + client.addListener(new Listener(){ + @Override + public void connected (Connection connection) { + Connect c = new Connect(); + c.id = connection.getID(); + c.addressTCP = connection.getRemoteAddressTCP().toString(); + + try{ + Net.handleClientReceived(c); + }catch (Exception e){ + Gdx.app.exit(); + throw new RuntimeException(e); + } + } + + @Override + public void disconnected (Connection connection) { + Disconnect c = new Disconnect(); + + try{ + Net.handleClientReceived(c); + }catch (Exception e){ + Gdx.app.exit(); + throw new RuntimeException(e); + } + } + + @Override + public void received (Connection connection, Object object) { + if(object instanceof FrameworkMessage) return; + + try{ + Net.handleClientReceived(object); + }catch (Exception e){ + Gdx.app.exit(); + throw new RuntimeException(e); + } + + } + }); + + register(Registrator.getClasses()); + } + + @Override + public void connect(String ip, int port) throws IOException { + client.connect(5000, ip, port, port); + } + + @Override + public void disconnect() { + client.close(); + } + + @Override + public void send(Object object, SendMode mode) { + if(mode == SendMode.tcp){ + client.sendTCP(object); + }else{ + client.sendUDP(object); + } + } + + @Override + public void updatePing() { + client.updateReturnTripTime(); + } + + @Override + public int getPing() { + return client.getReturnTripTime(); + } + + @Override + public Array
discover(){ + List list = client.discoverHosts(Vars.port, 5000); + ObjectSet hostnames = new ObjectSet<>(); + Array
result = new Array<>(); + + for(InetAddress a : list){ + if(!hostnames.contains(a.getHostName())) + result.add(new Address(a.getCanonicalHostName(), a.getHostAddress())); + hostnames.add(a.getHostName()); + } + + return result; + } + + @Override + public void register(Class... types) { + for(Class c : types){ + client.getKryo().register(c); + } + } + + @Override + public void dispose(){ + try { + client.dispose(); + }catch (IOException e){ + throw new RuntimeException(e); + } + } + +} diff --git a/kryonet/src/io/anuke/kryonet/KryoServer.java b/kryonet/src/io/anuke/kryonet/KryoServer.java new file mode 100644 index 0000000000..93879acaf1 --- /dev/null +++ b/kryonet/src/io/anuke/kryonet/KryoServer.java @@ -0,0 +1,181 @@ +package io.anuke.kryonet; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.utils.IntArray; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.FrameworkMessage; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Server; +import com.esotericsoftware.kryonet.util.InputStreamSender; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.net.Net.SendMode; +import io.anuke.mindustry.net.Net.ServerProvider; +import io.anuke.mindustry.net.Packets.Connect; +import io.anuke.mindustry.net.Packets.Disconnect; +import io.anuke.mindustry.net.Registrator; +import io.anuke.mindustry.net.Streamable; +import io.anuke.mindustry.net.Streamable.StreamBegin; +import io.anuke.mindustry.net.Streamable.StreamChunk; +import io.anuke.ucore.UCore; + +import java.io.IOException; +import java.util.Arrays; + +public class KryoServer implements ServerProvider { + Server server; + IntArray connections = new IntArray(); + + public KryoServer(){ + server = new Server(); + Thread thread = new Thread(server, "Kryonet Server"); + thread.setDaemon(true); + thread.start(); + server.addListener(new Listener(){ + + @Override + public void connected (Connection connection) { + Connect c = new Connect(); + c.id = connection.getID(); + c.addressTCP = connection.getRemoteAddressTCP().toString(); + + try { + Net.handleServerReceived(c, c.id); + connections.add(c.id); + }catch (Exception e){ + Gdx.app.exit(); + throw new RuntimeException(e); + } + } + + @Override + public void disconnected (Connection connection) { + Disconnect c = new Disconnect(); + c.id = connection.getID(); + + try{ + Net.handleServerReceived(c, c.id); + }catch (Exception e){ + Gdx.app.exit(); + throw new RuntimeException(e); + } + + connections.removeValue(c.id); + } + + @Override + public void received (Connection connection, Object object) { + if(object instanceof FrameworkMessage) return; + + try{ + Net.handleServerReceived(object, connection.getID()); + }catch (Exception e){ + Gdx.app.exit(); + throw new RuntimeException(e); + } + } + }); + + register(Registrator.getClasses()); + } + + @Override + public IntArray getConnections() { + return connections; + } + + @Override + public void host(int port) throws IOException { + server.bind(port, port); + } + + @Override + public void close() { + server.close(); + } + + @Override + public void sendStream(int id, Streamable stream) { + Connection connection = getByID(id); + + connection.addListener(new InputStreamSender(stream.stream, 512) { + int id; + + protected void start () { + //send an object so the receiving side knows how to handle the following chunks + StreamBegin begin = new StreamBegin(); + begin.total = stream.stream.available(); + begin.type = stream.getClass(); + connection.sendTCP(begin); + id = begin.id; + UCore.log("Sending begin packet: " + begin); + } + + protected Object next (byte[] bytes) { + StreamChunk chunk = new StreamChunk(); + chunk.id = id; + chunk.data = bytes; + UCore.log("Sending chunk of size " + chunk.data.length); + return chunk; //wrap the byte[] with an object so the receiving side knows how to handle it. + } + }); + } + + @Override + public void send(Object object, SendMode mode) { + if(mode == SendMode.tcp){ + server.sendToAllTCP(object); + }else{ + server.sendToAllUDP(object); + } + } + + @Override + public void sendTo(int id, Object object, SendMode mode) { + if(mode == SendMode.tcp){ + server.sendToTCP(id, object); + }else{ + server.sendToUDP(id, object); + } + } + + @Override + public void sendExcept(int id, Object object, SendMode mode) { + if(mode == SendMode.tcp){ + server.sendToAllExceptTCP(id, object); + }else{ + server.sendToAllExceptUDP(id, object); + } + } + + @Override + public int getPingFor(int connection) { + return getByID(connection).getReturnTripTime(); + } + + @Override + public void register(Class... types) { + for(Class c : types){ + server.getKryo().register(c); + } + } + + @Override + public void dispose(){ + try { + server.dispose(); + }catch (IOException e){ + throw new RuntimeException(e); + } + } + + Connection getByID(int id){ + for(Connection con : server.getConnections()){ + if(con.getID() == id){ + return con; + } + } + + throw new RuntimeException("Unable to find connection with ID " + id + "! Current connections: " + + Arrays.toString(server.getConnections())); + } +} diff --git a/settings.gradle b/settings.gradle index 58324176eb..b0219d0817 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ -include 'desktop', 'html', 'core', 'android' +include 'desktop', 'html', 'core', 'android', 'kryonet' if(System.properties["release"] == null || System.properties["release"].equals("false")){ if (new File(settingsDir, '../uCore').exists()) {