Merge branch 'master' into feature_rich_presence

This commit is contained in:
Jan Polák
2018-01-02 17:26:28 +01:00
committed by GitHub
52 changed files with 1898 additions and 760 deletions

1
.gitignore vendored
View File

@@ -21,6 +21,7 @@
*.war *.war
*.ear *.ear
hs_err_pid* hs_err_pid*
crash-report-*
## Robovm ## Robovm
/ios/robovm-build/ /ios/robovm-build/

View File

@@ -1,24 +1,25 @@
package io.anuke.mindustry; 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.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.telephony.TelephonyManager; 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.io.PlatformFunction;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.function.Callable; import io.anuke.ucore.function.Callable;
import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.TextField;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class AndroidLauncher extends AndroidApplication{ public class AndroidLauncher extends AndroidApplication{
boolean doubleScaleTablets = true; boolean doubleScaleTablets = true;
@@ -72,6 +73,9 @@ public class AndroidLauncher extends AndroidApplication{
} }
config.hideStatusBar = true; config.hideStatusBar = true;
Net.setClientProvider(new KryoClient());
Net.setServerProvider(new KryoServer());
initialize(new Mindustry(), config); initialize(new Mindustry(), config);
} }

View File

@@ -21,7 +21,7 @@ allprojects {
appName = "Mindustry" appName = "Mindustry"
gdxVersion = '1.9.8' gdxVersion = '1.9.8'
aiVersion = '1.8.1' aiVersion = '1.8.1'
uCoreVersion = '4fb2c63'; uCoreVersion = '8751c0a66c';
} }
repositories { repositories {
@@ -38,10 +38,10 @@ project(":desktop") {
dependencies { dependencies {
compile project(":core") compile project(":core")
compile project(":kryonet")
compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion"
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion" compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion"
compile "com.esotericsoftware:kryonet:2.22.0-RC1"
compile "com.github.MinnDevelopment:Java-DiscordRPC:dbd4aac" compile "com.github.MinnDevelopment:Java-DiscordRPC:dbd4aac"
} }
} }
@@ -71,6 +71,7 @@ project(":android") {
dependencies { dependencies {
compile project(":core") compile project(":core")
compile project(":kryonet")
compile "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" 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"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a" 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" natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
compile "com.badlogicgames.gdx:gdx-ai:$aiVersion" compile "com.badlogicgames.gdx:gdx-ai:$aiVersion"
compile "com.badlogicgames.gdx:gdx-controllers-android:$gdxVersion" 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 { tasks.eclipse.doLast {
delete ".project" delete ".project"
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

View File

@@ -12,7 +12,13 @@ text.savegame=Save Game
text.loadgame=Load Game text.loadgame=Load Game
text.joingame=Join Game text.joingame=Join Game
text.quit=Quit text.quit=Quit
text.server.connected=A player has joined.
text.server.disconnected={0} has disconnected.
text.hostserver=Host Server 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.title=Join Game
text.joingame.ip=IP: text.joingame.ip=IP:
text.disconnect=Disconnected. text.disconnect=Disconnected.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -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 } 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: { 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}, empty: { imageDownColor: accent, imageUpColor: white},
emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: lightgray}, emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: lightgray},
static: {up: button }, static: {up: button },

View File

@@ -1,16 +1,13 @@
package io.anuke.mindustry; package io.anuke.mindustry;
import java.util.Date;
import java.util.Locale;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.I18NBundle; import com.badlogic.gdx.utils.I18NBundle;
import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.*;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.io.PlatformFunction; import io.anuke.mindustry.io.PlatformFunction;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.*; import io.anuke.mindustry.world.blocks.*;
import io.anuke.ucore.UCore; 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.modules.ModuleCore;
import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.TextField;
import java.util.Date;
import java.util.Locale;
public class Mindustry extends ModuleCore { public class Mindustry extends ModuleCore {
public static Callable donationsCallable; public static Callable donationsCallable;
public static boolean hasDiscord = true; public static boolean hasDiscord = true;
@@ -90,16 +90,9 @@ public class Mindustry extends ModuleCore {
@Override @Override
public void render(){ public void render(){
super.render();
try{ if(!GameState.is(State.paused) || Net.active()){
super.render();
}catch (RuntimeException e){
//TODO display error log
//Gdx.app.getClipboard().setContents(e.getMessage());
throw e;
}
if(!GameState.is(State.paused)){
Timers.update(); Timers.update();
} }

View File

@@ -20,7 +20,7 @@ public class Vars{
//respawn time in frames //respawn time in frames
public static final float respawnduration = 60*4; public static final float respawnduration = 60*4;
//time between waves in frames (on normal mode) //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 //waves can last no longer than 3 minutes, otherwise the next one spawns
public static final float maxwavespace = 60*60*4f; public static final float maxwavespace = 60*60*4f;
//advance time the pathfinding starts at //advance time the pathfinding starts at
@@ -43,6 +43,7 @@ public class Vars{
public static final int zoomScale = Math.round(Unit.dp.scl(1)); 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 //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 debug = false;
public static boolean debugNet = false;
//whether the player can clip through walls //whether the player can clip through walls
public static boolean noclip = false; public static boolean noclip = false;
//whether to draw chunk borders //whether to draw chunk borders

View File

@@ -8,6 +8,7 @@ import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.SpawnPoint; import io.anuke.mindustry.world.SpawnPoint;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
@@ -101,20 +102,28 @@ public class Pathfind{
} }
public void update(){ public void update(){
int index = 0;
for(SpawnPoint point : Vars.control.getSpawnPoints()){ for(SpawnPoint point : Vars.control.getSpawnPoints()){
if(!point.request.pathFound){ if(!point.request.pathFound){
try{ try{
if(point.finder.search(point.request, ms)){ if(point.finder.search(point.request, ms)){
smoother.smoothPath(point.path); smoother.smoothPath(point.path);
point.pathTiles = point.path.nodes.toArray(Tile.class); 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){ }catch (ArrayIndexOutOfBoundsException e){
//no path //no path
point.request.pathFound = true; point.request.pathFound = true;
} }
} }
index ++;
} }
} }
public boolean finishedUpdating(){ public boolean finishedUpdating(){
@@ -133,7 +142,6 @@ public class Pathfind{
point.path.clear(); point.path.clear();
point.pathTiles = null; point.pathTiles = null;
point.tempTiles = null;
point.request = new PathFinderRequest<Tile>(point.start, Vars.control.getCore(), heuristic, point.path); point.request = new PathFinderRequest<Tile>(point.start, Vars.control.getCore(), heuristic, point.path);
point.request.statusChanged = true; //IMPORTANT! point.request.statusChanged = true; //IMPORTANT!
@@ -141,15 +149,15 @@ public class Pathfind{
} }
void findNode(Enemy enemy){ void findNode(Enemy enemy){
if(enemy.spawn >= Vars.control.getSpawnPoints().size){ if(enemy.lane >= Vars.control.getSpawnPoints().size){
enemy.spawn = 0; enemy.lane = 0;
} }
if(Vars.control.getSpawnPoints().get(enemy.spawn).pathTiles == null){ if(Vars.control.getSpawnPoints().get(enemy.lane).pathTiles == null){
return; 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); int closest = findClosest(enemy.path, enemy.x, enemy.y);

View File

@@ -49,7 +49,7 @@ public class Control extends Module{
final Array<Weapon> weapons = new Array<>(); final Array<Weapon> weapons = new Array<>();
final int[] items = new int[Item.getAllItems().size]; final int[] items = new int[Item.getAllItems().size];
public final EntityGroup<Enemy> enemyGroup = Entities.addGroup(Enemy.class); public final EntityGroup<Enemy> enemyGroup = Entities.addGroup(Enemy.class).enableMapping();
public final EntityGroup<TileEntity> tileGroup = Entities.addGroup(TileEntity.class, false); public final EntityGroup<TileEntity> tileGroup = Entities.addGroup(TileEntity.class, false);
public final EntityGroup<Bullet> bulletGroup = Entities.addGroup(Bullet.class); public final EntityGroup<Bullet> bulletGroup = Entities.addGroup(Bullet.class);
public final EntityGroup<Shield> shieldGroup = Entities.addGroup(Shield.class); public final EntityGroup<Shield> shieldGroup = Entities.addGroup(Shield.class);
@@ -187,7 +187,13 @@ public class Control extends Module{
for(int i = 0; i < Vars.saveSlots; i ++){ for(int i = 0; i < Vars.saveSlots; i ++){
Settings.defaults("save-" + i + "-autosave", true); Settings.defaults("save-" + i + "-autosave", true);
Settings.defaults("save-" + i + "-name", "untitled"); Settings.defaults("save-" + i + "-name", "untitled");
Settings.defaults("save-" + i + "-data", "empty");
} }
Settings.defaultList(
"ip", "localhost",
"port", Vars.port+""
);
Settings.loadAll("io.anuke.moment"); Settings.loadAll("io.anuke.moment");
@@ -196,6 +202,7 @@ public class Control extends Module{
} }
player = new Player(); player = new Player();
player.isAndroid = Vars.android;
player.isLocal = true; player.isLocal = true;
spawns = WaveCreator.getSpawns(); spawns = WaveCreator.getSpawns();
@@ -223,9 +230,8 @@ public class Control extends Module{
wavetime = waveSpacing(); wavetime = waveSpacing();
Entities.clear(); Entities.clear();
enemies = 0; enemies = 0;
if(!android) player.add();
player.add();
player.heal(); player.heal();
clearItems(); clearItems();
@@ -355,10 +361,11 @@ public class Control extends Module{
try{ try{
Enemy enemy = ClassReflection.newInstance(spawn.type); Enemy enemy = ClassReflection.newInstance(spawn.type);
enemy.set(tile.worldx() + Mathf.range(range), tile.worldy() + Mathf.range(range)); 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.tier = spawn.tier(wave, fl);
enemy.add();
Effects.effect(Fx.spawn, enemy); Effects.effect(Fx.spawn, enemy);
enemy.add(enemyGroup);
Vars.netServer.handleEnemySpawn(enemy); Vars.netServer.handleEnemySpawn(enemy);
@@ -404,6 +411,10 @@ public class Control extends Module{
} }
public void coreDestroyed(){ public void coreDestroyed(){
if(Net.active() && Net.server()){
Net.closeServer();
}
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y); Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
Sounds.play("corexplode"); Sounds.play("corexplode");
for(int i = 0; i < 16; i ++){ for(int i = 0; i < 16; i ++){
@@ -619,7 +630,7 @@ public class Control extends Module{
if(!GameState.is(State.menu)){ if(!GameState.is(State.menu)){
input.update(); 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); 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){ if(respawntime > 0){
@@ -657,7 +668,7 @@ public class Control extends Module{
if(enemies <= 0){ if(enemies <= 0){
wavetime -= delta(); wavetime -= delta();
if(lastUpdated < wave + 1 && wavetime < Vars.aheadPathfinding){ //start updatingbeforehand if(lastUpdated < wave + 1 && wavetime < Vars.aheadPathfinding){ //start updating beforehand
world.pathfinder().updatePath(); world.pathfinder().updatePath();
lastUpdated = wave + 1; lastUpdated = wave + 1;
} }

View File

@@ -1,25 +1,41 @@
package io.anuke.mindustry.core; package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx; 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.Vars;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Bullet;
import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.Player; 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;
import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Net.SendMode;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.net.Syncable;
import io.anuke.mindustry.net.Syncable.Interpolator;
import io.anuke.mindustry.resource.Weapon; import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.UCore; import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers; 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 io.anuke.ucore.modules.Module;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
public class NetClient extends Module { public class NetClient extends Module {
boolean connecting = false; boolean connecting = false;
boolean gotEntities = false; boolean gotEntities = false;
float playerSyncTime = 3; float playerSyncTime = 2;
float dataTimeout = 60*10;
public NetClient(){ public NetClient(){
@@ -30,6 +46,19 @@ public class NetClient extends Module {
Vars.ui.hideLoading(); Vars.ui.hideLoading();
Vars.ui.showLoading("$text.connecting.data"); 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 -> { Net.handle(Disconnect.class, packet -> {
@@ -38,6 +67,8 @@ public class NetClient extends Module {
Vars.ui.hideLoading(); Vars.ui.hideLoading();
}); });
GameState.set(State.menu);
Vars.ui.showError("$text.disconnect"); Vars.ui.showError("$text.disconnect");
connecting = false; connecting = false;
}); });
@@ -46,7 +77,8 @@ public class NetClient extends Module {
Net.handle(WorldData.class, data -> { Net.handle(WorldData.class, data -> {
Gdx.app.postRunnable(() -> { Gdx.app.postRunnable(() -> {
UCore.log("Recieved world data: " + data.stream.available() + " bytes."); 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); GameState.set(State.playing);
connecting = false; connecting = false;
@@ -58,7 +90,7 @@ public class NetClient extends Module {
Net.handle(EntityDataPacket.class, data -> { Net.handle(EntityDataPacket.class, data -> {
Gdx.app.postRunnable(() -> { 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); Vars.control.playerGroup.remap(Vars.player, data.playerid);
for (Player player : data.players) { for (Player player : data.players) {
@@ -76,11 +108,26 @@ public class NetClient extends Module {
Net.handle(SyncPacket.class, packet -> { Net.handle(SyncPacket.class, packet -> {
if(!gotEntities) return; if(!gotEntities) return;
//TODO awful code
for(int i = 0; i < packet.ids.length; i ++){ for(int i = 0; i < packet.ids.length; i ++){
int id = packet.ids[i]; int id = packet.ids[i];
if(id != Vars.player.id){ if(id != Vars.player.id){
Player player = Vars.control.playerGroup.getByID(id); Entity entity = null;
player.getInterpolator().type.read(player, packet.data[i]); 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); 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); 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); Vars.control.input.breakBlockInternal(packet.x, packet.y, false);
}); });
Net.handleServer(StateSyncPacket.class, packet -> { Net.handle(StateSyncPacket.class, packet -> {
//TODO replace with arraycopy() //TODO replace with arraycopy()
for(int i = 0; i < packet.items.length; i ++){ for(int i = 0; i < packet.items.length; i ++){
Vars.control.items[i] = packet.items[i]; Vars.control.items[i] = packet.items[i];
} }
Vars.control.setWaveData(packet.enemies, packet.wave, packet.countdown); 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(){ public void update(){
if(!Net.client()) return; if(!Net.client() || !Net.active()) return;
if(!GameState.is(State.menu) && Net.active()){ if(!GameState.is(State.menu) && Net.active()){
sync(); sync();
@@ -149,7 +291,7 @@ public class NetClient extends Module {
if(Timers.get("syncPlayer", playerSyncTime)){ if(Timers.get("syncPlayer", playerSyncTime)){
PositionPacket packet = new PositionPacket(); PositionPacket packet = new PositionPacket();
packet.data = Vars.player.getInterpolator().type.write(Vars.player); 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?
} }
} }
} }

View File

@@ -2,12 +2,16 @@ package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.IntArray;
import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.IntMap;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.enemies.Enemy; 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;
import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Net.SendMode;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
@@ -15,55 +19,90 @@ import io.anuke.mindustry.resource.ItemStack;
import io.anuke.mindustry.resource.Recipe; import io.anuke.mindustry.resource.Recipe;
import io.anuke.mindustry.resource.Weapon; import io.anuke.mindustry.resource.Weapon;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.UCore; import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.modules.Module; 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
public class NetServer extends Module{ public class NetServer extends Module{
/**Maps connection IDs to players.*/
IntMap<Player> connections = new IntMap<>(); IntMap<Player> connections = new IntMap<>();
float serverSyncTime = 4, itemSyncTime = 20, blockSyncTime = 120; float serverSyncTime = 4, itemSyncTime = 10, blockSyncTime = 120;
public NetServer(){ public NetServer(){
Net.handleServer(Connect.class, packet -> { Net.handleServer(Connect.class, connect -> {
UCore.log("Sending world data to client (ID="+packet.id+"/"+packet.addressTCP+")"); 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(); WorldData data = new WorldData();
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
SaveIO.write(stream); NetworkIO.write(stream);
UCore.log("Packed " + stream.size() + " uncompressed bytes of data."); UCore.log("Packed " + stream.size() + " uncompressed bytes of data.");
//TODO compress and uncompress when sending //TODO compress and uncompress when sending
data.stream = new ByteArrayInputStream(stream.toByteArray()); data.stream = new ByteArrayInputStream(stream.toByteArray());
Net.sendStream(packet.id, data); Net.sendStream(id, data);
Gdx.app.postRunnable(() -> { Gdx.app.postRunnable(() -> {
Vars.ui.showInfo("$text.server.connected");
EntityDataPacket dp = new EntityDataPacket(); EntityDataPacket dp = new EntityDataPacket();
Player player = new Player(); Player player = new Player();
player.clientid = packet.id; player.clientid = id;
player.set(Vars.player.x, Vars.player.y); player.name = packet.name;
player.placerot = Vars.player.placerot; player.isAndroid = packet.android;
player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2);
player.add(); player.add();
connections.put(packet.id, player); connections.put(id, player);
dp.playerid = player.id; dp.playerid = player.id;
dp.players = Vars.control.playerGroup.all().toArray(Player.class); dp.players = Vars.control.playerGroup.all().toArray(Player.class);
UCore.log("Sending entities: " + Arrays.toString(dp.players)); 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 -> { Net.handleServer(PositionPacket.class, pos -> {
Player player = connections.get(Net.getLastConnection()); Player player = connections.get(Net.getLastConnection());
player.getInterpolator().type.read(player, pos.data); player.getInterpolator().type.read(player, pos.data);
@@ -112,8 +151,58 @@ public class NetServer extends Module{
Net.send(packet, SendMode.udp); 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(){ public void update(){
@@ -130,11 +219,11 @@ public class NetServer extends Module{
if(Timers.get("serverSync", serverSyncTime)){ if(Timers.get("serverSync", serverSyncTime)){
SyncPacket packet = new SyncPacket(); 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.ids = new int[amount];
packet.data = new float[amount][0]; packet.data = new float[amount][0];
int index = 0; short index = 0;
for(Player player : Vars.control.playerGroup.all()){ for(Player player : Vars.control.playerGroup.all()){
float[] out = player.getInterpolator().type.write(player); float[] out = player.getInterpolator().type.write(player);
@@ -144,6 +233,16 @@ public class NetServer extends Module{
index ++; 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); Net.send(packet, SendMode.udp);
} }
@@ -153,25 +252,71 @@ public class NetServer extends Module{
packet.countdown = Vars.control.getWaveCountdown(); packet.countdown = Vars.control.getWaveCountdown();
packet.enemies = Vars.control.getEnemiesRemaining(); packet.enemies = Vars.control.getEnemiesRemaining();
packet.wave = Vars.control.getWave(); packet.wave = Vars.control.getWave();
packet.time = Timers.time();
packet.timestamp = TimeUtils.millis();
Net.send(packet, SendMode.udp); Net.send(packet, SendMode.udp);
} }
if(Timers.get("serverBlockSync", blockSyncTime)){ 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(); BlockSyncPacket packet = new BlockSyncPacket();
ByteArrayOutputStream bs = new ByteArrayOutputStream(); 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()); packet.stream = new ByteArrayInputStream(bs.toByteArray());
Net.sendStream(client, packet);
} }
} }

View File

@@ -418,10 +418,8 @@ public class Renderer extends RendererModule{
drawHealth(entity); drawHealth(entity);
} }
if(!Vars.android && Vars.showPlayer) { for(Player player : Vars.control.playerGroup.all()){
for(Player player : Vars.control.playerGroup.all()){ if(!player.isDead() && !player.isAndroid) drawHealth(player);
if(!player.isDead()) drawHealth(player);
}
} }
} }
} }

View File

@@ -24,6 +24,7 @@ import io.anuke.ucore.function.Listenable;
import io.anuke.ucore.modules.SceneModule; import io.anuke.ucore.modules.SceneModule;
import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.Element;
import io.anuke.ucore.scene.Skin; 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.build;
import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table; import io.anuke.ucore.scene.builders.table;
@@ -177,27 +178,6 @@ public class UI extends SceneModule{
editor = new MapEditor(); editor = new MapEditor();
editorDialog = new MapEditorDialog(editor); 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 = new Dialog("Warning", "dialog");
settingserror.content().add("[crimson]Failed to access local storage.\nSettings will not be saved."); 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 = new Dialog("$text.error.crashtitle", "dialog");
gameerror.content().labelWrap("$text.error.crashmessage").width(600f).pad(10f); gameerror.content().labelWrap("$text.error.crashmessage").width(600f).pad(10f);
gameerror.buttons().addButton("#text.ok", gameerror::hide).size(200f, 50); gameerror.buttons().addButton("#text.ok", gameerror::hide).size(200f, 50);
join = new JoinDialog();
discord = new Dialog("Discord", "dialog"); discord = new Dialog("Discord", "dialog");
discord.content().margin(12f); discord.content().margin(12f);
@@ -258,7 +240,7 @@ public class UI extends SceneModule{
prefs.hidden(()->{ prefs.hidden(()->{
if(!GameState.is(State.menu)){ if(!GameState.is(State.menu)){
if(!wasPaused) if(!wasPaused || Net.active())
GameState.set(State.playing); GameState.set(State.playing);
} }
}); });
@@ -269,7 +251,7 @@ public class UI extends SceneModule{
if(menu.getScene() != null){ if(menu.getScene() != null){
wasPaused = menu.wasPaused; wasPaused = menu.wasPaused;
} }
GameState.set(State.paused); if(!Net.active()) GameState.set(State.paused);
menu.hide(); menu.hide();
} }
}); });
@@ -467,6 +449,25 @@ public class UI extends SceneModule{
if(tooltip != null) if(tooltip != null)
tooltip.hide(); 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(){ public void showAbout(){
about.show(); about.show();

View File

@@ -1,25 +1,31 @@
package io.anuke.mindustry.core; package io.anuke.mindustry.core;
import static io.anuke.mindustry.Vars.*;
import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.ai.Pathfind; import io.anuke.mindustry.ai.Pathfind;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.io.Maps; import io.anuke.mindustry.io.Maps;
import io.anuke.mindustry.world.*; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.blocks.*; 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.Entities;
import io.anuke.ucore.entities.Entity; import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.modules.Module; import io.anuke.ucore.modules.Module;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp; import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.*;
public class World extends Module{ public class World extends Module{
private int seed; private int seed;
@@ -36,7 +42,8 @@ public class World extends Module{
@Override @Override
public void update(){ public void update(){
pathfind.update(); if(!(Net.active() && Net.client()))
pathfind.update();
} }
@Override @Override

View File

@@ -11,6 +11,7 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.ucore.core.*; import io.anuke.ucore.core.*;
import io.anuke.ucore.entities.DestructibleEntity; import io.anuke.ucore.entities.DestructibleEntity;
import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
@@ -19,10 +20,14 @@ import static io.anuke.mindustry.Vars.*;
public class Player extends DestructibleEntity implements Syncable{ public class Player extends DestructibleEntity implements Syncable{
private static final float speed = 1.1f; private static final float speed = 1.1f;
private static final float dashSpeed = 1.8f; private static final float dashSpeed = 1.8f;
public String name = "player name";
public transient Weapon weapon = Weapon.blaster; public transient Weapon weapon = Weapon.blaster;
public Mech mech = Mech.standard; public Mech mech = Mech.standard;
public float angle; public float angle;
public boolean isAndroid;
public transient float targetAngle = 0f;
public transient int clientid; public transient int clientid;
public transient boolean isLocal = false; public transient boolean isLocal = false;
@@ -43,15 +48,20 @@ public class Player extends DestructibleEntity implements Syncable{
} }
@Override @Override
public Interpolator getInterpolator() { public Interpolator<Player> getInterpolator() {
return inter; return inter;
} }
@Override @Override
public void damage(int amount){ public void damage(int amount){
if(!Vars.debug && !Vars.android) if(!Vars.debug)
super.damage(amount); super.damage(amount);
} }
@Override
public boolean collides(SolidEntity other){
return super.collides(other) && !isAndroid;
}
@Override @Override
public void onDeath(){ public void onDeath(){
@@ -79,20 +89,26 @@ public class Player extends DestructibleEntity implements Syncable{
@Override @Override
public void draw(){ 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")){ 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{ }else{
Draw.rect("mech-"+mech.name(), x, y, angle-90); Draw.rect(part+"-"+mech.name(), x, y, angle-90);
} }
} }
@Override @Override
public void update(){ public void update(){
if(!isLocal){ if(!isLocal || isAndroid){
if(!isDead()) inter.update(this); if(!isDead() && !isLocal) inter.update(this);
return; return;
} }
@@ -151,4 +167,9 @@ public class Player extends DestructibleEntity implements Syncable{
public Player add(){ public Player add(){
return add(Vars.control.playerGroup); return add(Vars.control.playerGroup);
} }
@Override
public String toString() {
return "Player{" + id + ", android=" + isAndroid + ", local=" + isLocal + ", " + x + ", " + y + "}\n";
}
} }

View File

@@ -1,12 +1,9 @@
package io.anuke.mindustry.entities; 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.Vars;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Fx;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; 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.Mathf;
import io.anuke.ucore.util.Timer; import io.anuke.ucore.util.Timer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class TileEntity extends Entity{ public class TileEntity extends Entity{
public Tile tile; public Tile tile;
public int[] items = new int[Item.getAllItems().size]; public int[] items = new int[Item.getAllItems().size];
@@ -54,19 +55,28 @@ public class TileEntity extends Entity{
} }
public void onDeath(){ public void onDeath(){
onDeath(false);
if(tile.block() == ProductionBlocks.core){ }
Vars.control.coreDestroyed();
public void onDeath(boolean force){
if(Net.active() && Net.server()){
Vars.netServer.handleBlockDestroyed(this);
} }
if(!dead) { if(!Net.active() || Net.server() || force){
dead = true; if(tile.block() == ProductionBlocks.core){
Block block = tile.block(); Vars.control.coreDestroyed();
}
block.onDestroyed(tile); if(!dead) {
dead = true;
Block block = tile.block();
Vars.world.removeBlock(tile); block.onDestroyed(tile);
remove();
Vars.world.removeBlock(tile);
remove();
}
} }
} }
@@ -80,6 +90,10 @@ public class TileEntity extends Entity{
int amount = tile.block().handleDamage(tile, damage); int amount = tile.block().handleDamage(tile, damage);
health -= amount; health -= amount;
if(health <= 0) onDeath(); if(health <= 0) onDeath();
if(Net.active() && Net.server()){
Vars.netServer.handleBlockDamaged(this);
}
} }
public boolean collide(Bullet other){ public boolean collide(Bullet other){

View File

@@ -3,21 +3,32 @@ package io.anuke.mindustry.entities.enemies;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.Vars; 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.Fx;
import io.anuke.mindustry.graphics.Shaders; 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.Tile;
import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.ucore.UCore; import io.anuke.ucore.core.Draw;
import io.anuke.ucore.core.*; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.entities.*; import io.anuke.ucore.core.Graphics;
import io.anuke.ucore.util.*; 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; 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 Color[] tierColors = { Color.valueOf("ffe451"), Color.valueOf("f48e20"), Color.valueOf("ff6757"), Color.valueOf("ff2d86") };
public final static int maxtier = 4; public final static int maxtier = 4;
public final static float maxIdle = 60*1.5f; public final static float maxIdle = 60*1.5f;
@@ -37,17 +48,19 @@ public class Enemy extends DestructibleEntity{
protected int damage; protected int damage;
protected Enemy spawner; protected Enemy spawner;
protected int spawned = 0; protected int spawned = 0;
protected float angle;
protected boolean targetCore = false; protected boolean targetCore = false;
protected boolean stopNearCore = true; protected boolean stopNearCore = true;
protected float mass = 1f; protected float mass = 1f;
protected String className; protected String className;
protected Interpolator<Enemy> inter = new Interpolator(SyncType.enemy);
public float idletime = 0f; public float idletime = 0f;
public int spawn; public int lane;
public int node = -1; public int node = -1;
public Tile[] path; public Tile[] path;
public float angle;
public float xvelocity, yvelocity; public float xvelocity, yvelocity;
public Entity target; public Entity target;
public int tier = 1; public int tier = 1;
@@ -65,11 +78,20 @@ public class Enemy extends DestructibleEntity{
className = ClassReflection.getSimpleName(getClass()).toLowerCase(); className = ClassReflection.getSimpleName(getClass()).toLowerCase();
} }
public Interpolator<Enemy> getInterpolator() {
return inter;
}
public float drawSize(){ public float drawSize(){
return 12; return 12;
} }
void move(){ void move(){
if(Net.client() && Net.active()){
inter.update(this);
return;
}
Tile core = Vars.control.getCore(); Tile core = Vars.control.getCore();
if(idletime > maxIdleLife){ if(idletime > maxIdleLife){
@@ -152,9 +174,21 @@ public class Enemy extends DestructibleEntity{
} }
void shoot(BulletType bullet, float rotation){ 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(); if(!(Net.active() && Net.client())) {
out.damage = (int) (damage * Vars.multiplier); 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 @Override
@@ -184,6 +218,10 @@ public class Enemy extends DestructibleEntity{
Effects.sound("bang2", this); Effects.sound("bang2", this);
remove(); remove();
dead = true; dead = true;
if(Net.active() && Net.server()){
Vars.netServer.handleEnemyDeath(this);
}
} }
@Override @Override
@@ -258,7 +296,7 @@ public class Enemy extends DestructibleEntity{
} }
@Override @Override
public <T extends Entity> T add(){ public Enemy add(){
return (T) add(Vars.control.enemyGroup); return add(Vars.control.enemyGroup);
} }
} }

View File

@@ -38,7 +38,7 @@ public class FortressEnemy extends Enemy{
Angles.translation(angle, 20f); Angles.translation(angle, 20f);
FastEnemy enemy = new FastEnemy(); FastEnemy enemy = new FastEnemy();
enemy.spawn = spawn; enemy.lane = lane;
enemy.tier = this.tier; enemy.tier = this.tier;
enemy.spawner = this; enemy.spawner = this;
enemy.set(x + Angles.x(), y + Angles.y()); enemy.set(x + Angles.x(), y + Angles.y());
@@ -52,10 +52,9 @@ public class FortressEnemy extends Enemy{
speed = 0.001f; speed = 0.001f;
} }
} }
@Override
public void shoot(BulletType type){ void onShoot(BulletType type, float rotation){
super.shoot(bullet);
Effects.effect(Fx.largeCannonShot, x + Angles.x(), y + Angles.y(), angle); Effects.effect(Fx.largeCannonShot, x + Angles.x(), y + Angles.y(), angle);
Effects.shake(3f, 3f, this); Effects.shake(3f, 3f, this);
} }

View File

@@ -1,15 +1,15 @@
package io.anuke.mindustry.input; package io.anuke.mindustry.input;
import static io.anuke.mindustry.Vars.*;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.input.GestureDetector.GestureAdapter; import com.badlogic.gdx.input.GestureDetector.GestureAdapter;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Inputs; import io.anuke.ucore.core.Inputs;
import io.anuke.ucore.scene.ui.layout.Unit; 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{ public class GestureHandler extends GestureAdapter{
AndroidInput input; 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) && if(!Vars.control.showCursor() && !(player.recipe != null && Vars.control.hasItems(player.recipe.requirements) && player.placeMode.lockCamera) &&
!(player.recipe == null && player.breakMode.lockCamera)){ !(player.recipe == null && player.breakMode.lockCamera)){
player.x -= deltaX*Core.camera.zoom/Core.cameraScale; float dx = deltaX*Core.camera.zoom/Core.cameraScale, dy = deltaY*Core.camera.zoom/Core.cameraScale;
player.y += 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)){ }else if(player.placeMode.lockCamera && (player.placeMode.pan && player.recipe != null)){
input.mousex += deltaX; input.mousex += deltaX;
input.mousey += deltaY; input.mousey += deltaY;

View File

@@ -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);
}
}
}

View File

@@ -1,28 +1,31 @@
package io.anuke.mindustry.io; 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.files.FileHandle;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.utils.TimeUtils;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.enemies.*; import io.anuke.mindustry.entities.enemies.*;
import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.resource.Weapon; 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.mindustry.world.blocks.Blocks;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Settings;
import io.anuke.ucore.entities.Entities; 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: * Save format:
* *
@@ -84,7 +87,7 @@ public class SaveIO{
private static final int fileVersionID = 12; private static final int fileVersionID = 12;
//TODO automatic registration of types? //TODO automatic registration of types?
private static final Array<Class<? extends Enemy>> enemyIDs = Array.with( public static final Array<Class<? extends Enemy>> enemyIDs = Array.with(
Enemy.class, Enemy.class,
FastEnemy.class, FastEnemy.class,
RapidEnemy.class, RapidEnemy.class,
@@ -98,78 +101,78 @@ public class SaveIO{
EmpEnemy.class EmpEnemy.class
); );
private static final ObjectMap<Class<? extends Enemy>, Byte> idEnemies = new ObjectMap<Class<? extends Enemy>, Byte>(){{ public static final ObjectMap<Class<? extends Enemy>, Byte> idEnemies = new ObjectMap<Class<? extends Enemy>, Byte>(){{
for(int i = 0; i < enemyIDs.size; i ++){ for(int i = 0; i < enemyIDs.size; i ++){
put(enemyIDs.get(i), (byte)i); put(enemyIDs.get(i), (byte)i);
} }
}}; }};
public static void saveToSlot(int slot){ 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){ 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){ public static boolean isSaveValid(int slot){
return isSaveValid(fileFor(slot)); try {
} return isSaveValid(readSlotMeta(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;
}catch (Exception e){ }catch (Exception e){
return false; return false;
} }
} }
public static String getTimeString(int slot){ public static boolean isSaveValid(FileHandle file){
return isSaveValid(new DataInputStream(file.read()));
try(DataInputStream stream = new DataInputStream(fileFor(slot).read())){ }
stream.readInt();
Date date = new Date(stream.readLong()); public static boolean isSaveValid(DataInputStream stream){
return Mindustry.platforms.format(date);
}catch (IOException e){ try{
throw new RuntimeException(e); SaveMeta meta = getData(stream);
return meta.version == fileVersionID && meta.map != null;
}catch (Exception e){
return false;
} }
} }
public static int getWave(int slot){ public static SaveMeta getData(int slot){
return getData(readSlotMeta(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 GameMode getMode(int slot){ public static SaveMeta getData(DataInputStream stream){
try(DataInputStream stream = new DataInputStream(fileFor(slot).read())){ try{
stream.readInt(); //read version int version = stream.readInt(); //read version
stream.readLong(); //read last saved time long time = stream.readLong(); //read last saved time
return GameMode.values()[stream.readByte()]; //read the gamemode byte mode = stream.readByte(); //read the gamemode
}catch (IOException e){ byte map = stream.readByte(); //read the map
throw new RuntimeException(e); int wave = stream.readInt(); //read the wave
} return new SaveMeta(version, time, mode, map, wave);
}
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
}catch (IOException e){ }catch (IOException e){
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -245,7 +248,7 @@ public class SaveIO{
for(Enemy enemy : Vars.control.enemyGroup.all()){ for(Enemy enemy : Vars.control.enemyGroup.all()){
if(idEnemies.containsKey(enemy.getClass())){ if(idEnemies.containsKey(enemy.getClass())){
stream.writeByte(idEnemies.get(enemy.getClass())); //type 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.x); //x
stream.writeFloat(enemy.y); //y stream.writeFloat(enemy.y); //y
stream.writeByte(enemy.tier); //tier stream.writeByte(enemy.tier); //tier
@@ -391,7 +394,7 @@ public class SaveIO{
try{ try{
Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type));
enemy.spawn = lane; enemy.lane = lane;
enemy.health = health; enemy.health = health;
enemy.x = x; enemy.x = x;
enemy.y = y; enemy.y = y;

View File

@@ -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;
}
}

View File

@@ -2,12 +2,7 @@ package io.anuke.mindustry.io;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array; 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.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.Vars;
import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
@@ -31,7 +26,9 @@ public class Saves {
saves.clear(); saves.clear();
for(int i = 0; i < Vars.saveSlots; i ++){ for(int i = 0; i < Vars.saveSlots; i ++){
if(SaveIO.isSaveValid(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; nextSlot = i + 1;
} }
} }
@@ -78,6 +75,7 @@ public class Saves {
slot.setName(name); slot.setName(name);
saves.add(slot); saves.add(slot);
SaveIO.saveToSlot(slot.index); SaveIO.saveToSlot(slot.index);
slot.meta = SaveIO.getData(slot.index);
} }
public Array<SaveSlot> getSaveSlots(){ public Array<SaveSlot> getSaveSlots(){
@@ -86,6 +84,7 @@ public class Saves {
public class SaveSlot{ public class SaveSlot{
public final int index; public final int index;
SaveMeta meta;
public SaveSlot(int index){ public SaveSlot(int index){
this.index = index; this.index = index;
@@ -94,19 +93,21 @@ public class Saves {
public void load(){ public void load(){
current = this; current = this;
SaveIO.loadFromSlot(index); SaveIO.loadFromSlot(index);
meta = SaveIO.getData(index);
} }
public void save(){ public void save(){
current = this; current = this;
SaveIO.saveToSlot(index); SaveIO.saveToSlot(index);
meta = SaveIO.getData(index);
} }
public String getDate(){ public String getDate(){
return SaveIO.getTimeString(index); return meta.date;
} }
public Map getMap(){ public Map getMap(){
return SaveIO.getMap(index); return meta.map;
} }
public String getName(){ public String getName(){
@@ -119,11 +120,11 @@ public class Saves {
} }
public int getWave(){ public int getWave(){
return SaveIO.getWave(index); return meta.wave;
} }
public GameMode getMode(){ public GameMode getMode(){
return SaveIO.getMode(index); return meta.mode;
} }
public boolean isAutosave(){ public boolean isAutosave(){
@@ -156,6 +157,7 @@ public class Saves {
public void delete(){ public void delete(){
SaveIO.fileFor(index).delete(); SaveIO.fileFor(index).delete();
saves.removeValue(this, true);
if(this == current){ if(this == current){
current = null; current = null;
} }

View File

@@ -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;
}
}

View File

@@ -1,8 +1,11 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
import com.badlogic.gdx.Gdx; 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.IntMap;
import com.badlogic.gdx.utils.ObjectMap; 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.StreamBegin;
import io.anuke.mindustry.net.Streamable.StreamBuilder; import io.anuke.mindustry.net.Streamable.StreamBuilder;
import io.anuke.mindustry.net.Streamable.StreamChunk; import io.anuke.mindustry.net.Streamable.StreamChunk;
@@ -21,6 +24,7 @@ public class Net{
private static int lastConnection = -1; private static int lastConnection = -1;
private static IntMap<StreamBuilder> streams = new IntMap<>(); private static IntMap<StreamBuilder> streams = new IntMap<>();
private static AsyncExecutor executor = new AsyncExecutor(4);
/**Connect to an address.*/ /**Connect to an address.*/
public static void connect(String ip, int port) throws IOException{ public static void connect(String ip, int port) throws IOException{
@@ -48,6 +52,23 @@ public class Net{
server = false; server = false;
active = 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<Array<Address>> cons){
executor.submit(() -> {
Array<Address> 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.*/ /**Send an object to all connected clients, or to the server if this is a client.*/
public static void send(Object object, SendMode mode){ public static void send(Object object, SendMode mode){
@@ -146,6 +167,12 @@ public class Net{
return !server; return !server;
} }
public static void dispose(){
clientProvider.dispose();
serverProvider.dispose();
executor.dispose();
}
/**Register classes that will be sent. Must be done for all classes.*/ /**Register classes that will be sent. Must be done for all classes.*/
public static void registerClasses(Class<?>... classes){ public static void registerClasses(Class<?>... classes){
clientProvider.register(classes); clientProvider.register(classes);
@@ -164,8 +191,12 @@ public class Net{
public int getPing(); public int getPing();
/**Disconnect from the server.*/ /**Disconnect from the server.*/
public void disconnect(); 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<Address> discover();
/**Register classes to be sent.*/ /**Register classes to be sent.*/
public void register(Class<?>... types); public void register(Class<?>... types);
/**Close all connections.*/
public void dispose();
} }
/**Server implementation.*/ /**Server implementation.*/
@@ -182,8 +213,14 @@ public class Net{
public void sendExcept(int id, Object object, SendMode mode); public void sendExcept(int id, Object object, SendMode mode);
/**Close the server connection.*/ /**Close the server connection.*/
public void close(); public void close();
/**Return all connected users.*/
public IntArray getConnections();
/**Register classes to be sent.*/ /**Register classes to be sent.*/
public void register(Class<?>... types); 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{ public enum SendMode{

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.net; package io.anuke.mindustry.net;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.enemies.Enemy;
/**Class for storing all packets.*/ /**Class for storing all packets.*/
public class Packets { public class Packets {
@@ -27,16 +28,27 @@ public class Packets {
public static class SyncPacket{ public static class SyncPacket{
public int[] ids; public int[] ids;
public float[][] data; public float[][] data;
public short enemyStart;
} }
public static class BlockSyncPacket extends Streamable{ 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 int[] items;
public float countdown; public float countdown, time;
public int enemies, wave; public int enemies, wave;
public long timestamp;
} }
public static class PositionPacket{ public static class PositionPacket{
@@ -55,6 +67,12 @@ public class Packets {
public int playerid; public int playerid;
} }
public static class BulletPacket{
public int type, owner;
public float x, y, angle;
public short damage;
}
public static class PlacePacket{ public static class PlacePacket{
public int playerid; public int playerid;
public byte rotation; public byte rotation;
@@ -66,4 +84,28 @@ public class Packets {
public int playerid; public int playerid;
public short x, y; public short x, y;
} }
public static class EnemySpawnPacket{
public Class<? extends Enemy> 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;
}
} }

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.net;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.enemies.*;
import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.net.Streamable.StreamBegin; import io.anuke.mindustry.net.Streamable.StreamBegin;
import io.anuke.mindustry.net.Streamable.StreamChunk; import io.anuke.mindustry.net.Streamable.StreamChunk;
@@ -24,12 +25,21 @@ public class Registrator {
BreakPacket.class, BreakPacket.class,
StateSyncPacket.class, StateSyncPacket.class,
BlockSyncPacket.class, BlockSyncPacket.class,
EnemySpawnPacket.class,
PathPacket.class,
BulletPacket.class,
EnemyDeathPacket.class,
BlockUpdatePacket.class,
BlockDestroyPacket.class,
ConnectPacket.class,
DisconnectPacket.class,
Class.class, Class.class,
byte[].class, byte[].class,
float[].class, float[].class,
float[][].class, float[][].class,
int[].class, int[].class,
int[][].class,
Entity[].class, Entity[].class,
Player[].class, Player[].class,
Array.class, Array.class,
@@ -37,7 +47,19 @@ public class Registrator {
Entity.class, Entity.class,
Player.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
}; };
} }
} }

View File

@@ -2,9 +2,15 @@ package io.anuke.mindustry.net;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.entities.Player; 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.entities.Entity;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
//TODO clean up this giant mess
public interface Syncable { public interface Syncable {
public Interpolator<?> getInterpolator(); public Interpolator<?> getInterpolator();
@@ -29,6 +35,37 @@ public interface Syncable {
@Override @Override
public void update(Player entity, Interpolator interpolator) { 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> enemy = new SyncType<Enemy>() {
@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(); Interpolator i = entity.getInterpolator();
if(i.target.dst(entity.x, entity.y) > 16){ if(i.target.dst(entity.x, entity.y) > 16){
entity.set(i.target.x, i.target.y); entity.set(i.target.x, i.target.y);
@@ -44,6 +81,7 @@ public interface Syncable {
public static class Interpolator<T extends Entity> { public static class Interpolator<T extends Entity> {
public SyncType<T> type; public SyncType<T> type;
public Vector2 target = new Vector2(); public Vector2 target = new Vector2();
public Vector2 last = new Vector2();
public float targetrot; public float targetrot;
public Interpolator(SyncType<T> type){ public Interpolator(SyncType<T> type){

View File

@@ -13,10 +13,10 @@ public class Item{
steel = new Item("steel"), steel = new Item("steel"),
titanium = new Item("titanium"), titanium = new Item("titanium"),
dirium = new Item("dirium"), dirium = new Item("dirium"),
uranium = new Item("uranium"), uranium = new Item("uranium");
sand = new Item("sand"), /*sand = new Item("sand"),
glass = new Item("glass"), glass = new Item("glass"),
silicon = new Item("silicon"); silicon = new Item("silicon");*/
public final int id; public final int id;
public final String name; public final String name;

View File

@@ -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<Address> 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();
}
});
}
}

View File

@@ -1,23 +1,18 @@
package io.anuke.mindustry.ui; package io.anuke.mindustry.ui;
import static io.anuke.mindustry.Vars.ui;
import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.Element;
import io.anuke.ucore.scene.builders.build; import io.anuke.ucore.scene.builders.build;
import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.ui.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.Bundles;
import io.anuke.ucore.util.Strings;
import java.io.IOException; import static io.anuke.mindustry.Vars.ui;
public class MenuDialog extends FloatingDialog{ public class MenuDialog extends FloatingDialog{
private SaveDialog save = new SaveDialog(); private SaveDialog save = new SaveDialog();
@@ -25,14 +20,14 @@ public class MenuDialog extends FloatingDialog{
public boolean wasPaused = false; public boolean wasPaused = false;
public MenuDialog() { public MenuDialog() {
super("Paused"); super("$text.menu");
setup(); setup();
} }
void setup(){ void setup(){
shown(() -> { shown(() -> {
wasPaused = GameState.is(State.paused); wasPaused = GameState.is(State.paused);
GameState.set(State.paused); if(!Net.active()) GameState.set(State.paused);
}); });
if(!Vars.android){ if(!Vars.android){
@@ -40,7 +35,7 @@ public class MenuDialog extends FloatingDialog{
content().addButton("$text.back", () -> { content().addButton("$text.back", () -> {
hide(); hide();
if(!wasPaused) if(!wasPaused || Net.active())
GameState.set(State.playing); GameState.set(State.playing);
}); });
@@ -49,39 +44,25 @@ public class MenuDialog extends FloatingDialog{
ui.showPrefs(); ui.showPrefs();
}); });
if(!Vars.gwt){ content().row();
content().row(); content().addButton("$text.savegame", () -> {
content().addButton("$text.savegame", () -> { save.show();
save.show(); });
});
content().row(); content().row();
content().addButton("$text.loadgame", () -> { content().addButton("$text.loadgame", () -> {
load.show(); load.show();
}); });
}
content().row(); content().row();
content().addButton("$text.hostserver", () -> { content().addButton("$text.hostserver", () -> ui.showHostServer())
Vars.ui.showTextInput("$text.hostserver", "$text.server.port", Vars.port + "", new DigitsOnlyFilter(), text -> { .disabled(b -> Net.active() || (Net.active() && !Net.server()));
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().row(); content().row();
content().addButton("$text.quit", () -> { content().addButton("$text.quit", () -> {
Vars.ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> { ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> {
runSave(); runSave();
hide(); hide();
GameState.set(State.menu); 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-tools", isize, () -> ui.showPrefs()).text("$text.settings").padTop(4f);
new imagebutton("icon-save", isize, ()-> save.show()).text("$text.save").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-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, () -> { new imagebutton("icon-quit", isize, () -> {
Vars.ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> { Vars.ui.showConfirm("$text.confirm", "$text.quit.confirm", () -> {

View File

@@ -1,34 +1,26 @@
package io.anuke.mindustry.ui.fragments; 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.Gdx;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.math.Interpolation;
import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.GameMode;
import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Draw;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.actions.Actions;
import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.imagebutton;
import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.label;
import io.anuke.ucore.scene.builders.table; import io.anuke.ucore.scene.builders.table;
import io.anuke.ucore.scene.event.Touchable; 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.ImageButton;
import io.anuke.ucore.scene.ui.Label; 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.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import static io.anuke.mindustry.Vars.*;
public class HudFragment implements Fragment{ public class HudFragment implements Fragment{
private ImageButton menu, flip, pause; private ImageButton menu, flip, pause;
private Table respawntable; private Table respawntable;
@@ -74,7 +66,8 @@ public class HudFragment implements Fragment{
pause = new imagebutton("icon-pause", isize, ()->{ pause = new imagebutton("icon-pause", isize, ()->{
GameState.set(GameState.is(State.paused) ? State.playing : State.paused); 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(); }}.end();
@@ -105,7 +98,7 @@ public class HudFragment implements Fragment{
//paused table //paused table
new table(){{ new table(){{
visible(()->GameState.is(State.paused)); visible(()->GameState.is(State.paused) && !Net.active());
atop(); atop();
new table("pane"){{ new table("pane"){{
@@ -157,6 +150,14 @@ public class HudFragment implements Fragment{
}}.end(); }}.end();
if(Vars.debugNet) {
new table() {{
new label(() -> "players: " + Vars.control.playerGroup.amount());
row();
new label(() -> "" + Vars.control.playerGroup.all());
}}.end();
}
blockfrag.build(); blockfrag.build();
} }

View File

@@ -1,9 +1,6 @@
package io.anuke.mindustry.ui.fragments; package io.anuke.mindustry.ui.fragments;
import static io.anuke.mindustry.Vars.*;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState;
import io.anuke.mindustry.core.GameState.State; 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.imagebutton;
import io.anuke.ucore.scene.builders.table; import io.anuke.ucore.scene.builders.table;
import static io.anuke.mindustry.Vars.*;
public class MenuFragment implements Fragment{ public class MenuFragment implements Fragment{
public void build(){ public void build(){
@@ -35,11 +34,11 @@ public class MenuFragment implements Fragment{
add(new MenuButton("$text.tutorial", group, ()-> control.playMap(world.maps().getMap("tutorial")))); add(new MenuButton("$text.tutorial", group, ()-> control.playMap(world.maps().getMap("tutorial"))));
row(); row();
add(new MenuButton("$text.loadgame", group, ui::showLoadGame));
row();
if(!gwt){ if(!gwt){
add(new MenuButton("$text.loadgame", group, ui::showLoadGame));
row();
add(new MenuButton("$text.editor", group, ui::showEditor)); add(new MenuButton("$text.editor", group, ui::showEditor));
row(); 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-editor", isize, () -> ui.showEditor()).text("$text.editor").padTop(4f);
new imagebutton("icon-tools", isize, () -> ui.showPrefs()).text("$text.settings").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){ if(Mindustry.donationsCallable != null){
new imagebutton("icon-donate", isize, () -> { new imagebutton("icon-donate", isize, () -> {

View File

@@ -17,7 +17,7 @@ import io.anuke.ucore.noise.Noise;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
public class Generator{ public class Generator{
static final ObjectMap<Block, Block> rocks = new ObjectMap(){{ public static final ObjectMap<Block, Block> rocks = new ObjectMap(){{
put(Blocks.stone, Blocks.rock); put(Blocks.stone, Blocks.rock);
put(Blocks.snow, Blocks.icerock); put(Blocks.snow, Blocks.icerock);
put(Blocks.grass, Blocks.shrub); put(Blocks.grass, Blocks.shrub);

View File

@@ -8,7 +8,6 @@ import io.anuke.mindustry.ai.SmoothGraphPath;
public class SpawnPoint{ public class SpawnPoint{
public Tile start; public Tile start;
public Tile[] pathTiles; public Tile[] pathTiles;
public Tile[] tempTiles;
public PathFinder<Tile> finder; public PathFinder<Tile> finder;
public SmoothGraphPath path = new SmoothGraphPath(); public SmoothGraphPath path = new SmoothGraphPath();
public PathFinderRequest<Tile> request; public PathFinderRequest<Tile> request;

View File

@@ -1,16 +1,15 @@
package io.anuke.mindustry.world; package io.anuke.mindustry.world;
import static io.anuke.mindustry.Vars.tilesize;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.Bits;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.tilesize;
public class Tile{ public class Tile{
private static final Array<Tile> tmpArray = new Array<>(); private static final Array<Tile> tmpArray = new Array<>();
@@ -34,6 +33,10 @@ public class Tile{
this(x, y); this(x, y);
iSetFloor(floor); iSetFloor(floor);
} }
public int packedPosition(){
return x + y * Vars.world.width();
}
private void iSetFloor(Block floor){ private void iSetFloor(Block floor){
byte id = (byte)floor.id; byte id = (byte)floor.id;

View File

@@ -138,34 +138,23 @@ public class Blocks{
solid = true; solid = true;
}}, }},
shrub = new Block("shrub"){{ shrub = new Rock("shrub"){
shadow = "shrubshadow";
breakable = true; },
breaktime = 10;
}},
rock = new Block("rock"){{ rock = new Rock("rock"){{
shadow = "rockshadow";
breakable = true;
breaktime = 15;
variants = 2; variants = 2;
varyShadow = true; varyShadow = true;
drops = new ItemStack(Item.stone, 3); drops = new ItemStack(Item.stone, 3);
}}, }},
icerock = new Block("icerock"){{ icerock = new Rock("icerock"){{
shadow = "rockshadow";
breakable = true;
breaktime = 15;
variants = 2; variants = 2;
varyShadow = true; varyShadow = true;
drops = new ItemStack(Item.stone, 3); drops = new ItemStack(Item.stone, 3);
}}, }},
blackrock = new Block("blackrock"){{ blackrock = new Rock("blackrock"){{
shadow = "blackrockshadow";
breakable = true;
breaktime = 15;
variants = 1; variants = 1;
varyShadow = true; varyShadow = true;
drops = new ItemStack(Item.stone, 3); drops = new ItemStack(Item.stone, 3);

View File

@@ -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;
}
}

View File

@@ -1,15 +1,7 @@
package io.anuke.mindustry.world.blocks.types.distribution; 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.Array;
import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.IntArray;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.world.Block; 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.mindustry.world.Tile;
import io.anuke.ucore.core.Draw; import io.anuke.ucore.core.Draw;
import io.anuke.ucore.core.Timers; 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{ public class Conveyor extends Block{
private static ItemPos pos1 = new ItemPos(); private static ItemPos pos1 = new ItemPos();
@@ -182,6 +187,7 @@ public class Conveyor extends Block{
@Override @Override
public void read(DataInputStream stream) throws IOException{ public void read(DataInputStream stream) throws IOException{
convey.clear();
int amount = stream.readInt(); int amount = stream.readInt();
convey.ensureCapacity(amount); convey.ensureCapacity(amount);

View File

@@ -1,15 +1,14 @@
package io.anuke.mindustry.world.blocks.types.production; package io.anuke.mindustry.world.blocks.types.production;
import java.util.Arrays;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Fx;
import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import java.util.Arrays;
public class Crafter extends Block{ public class Crafter extends Block{
protected final int timerDump = timers++; protected final int timerDump = timers++;
@@ -32,7 +31,7 @@ public class Crafter extends Block{
@Override @Override
public void update(Tile tile){ 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); tryDump(tile, -1, result);
} }
@@ -52,13 +51,11 @@ public class Crafter extends Block{
@Override @Override
public boolean acceptItem(Item item, Tile dest, Tile source){ public boolean acceptItem(Item item, Tile dest, Tile source){
boolean craft = false;
for(Item req : requirements){ for(Item req : requirements){
if(item == req){ if(item == req){
craft = true; return true;
break;
} }
} }
return craft; return false;
} }
} }

View File

@@ -75,7 +75,7 @@ public class LiquidCrafter extends LiquidBlock{
Effects.effect(craftEffect, tile.worldx(), tile.worldy()); Effects.effect(craftEffect, tile.worldx(), tile.worldy());
} }
if(entity.timer.get(timerDump, 30)){ if(entity.timer.get(timerDump, 15)){
tryDump(tile, -1, output); tryDump(tile, -1, output);
} }
} }

View File

@@ -6,32 +6,25 @@ import club.minnced.discord.rpc.DiscordRichPresence;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.esotericsoftware.kryonet.*; import io.anuke.kryonet.KryoClient;
import com.esotericsoftware.kryonet.util.InputStreamSender; import io.anuke.kryonet.KryoServer;
import com.esotericsoftware.minlog.Log;
import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Mindustry;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.io.PlatformFunction; import io.anuke.mindustry.io.PlatformFunction;
import io.anuke.mindustry.net.Net; 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.scene.ui.TextField;
import io.anuke.ucore.util.Strings;
import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
public class DesktopLauncher { public class DesktopLauncher {
@@ -74,219 +67,57 @@ public class DesktopLauncher {
@Override @Override
public void onSceneChange(String state, String details, String icon) { 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(); DiscordRichPresence presence = new DiscordRichPresence();
presence.startTimestamp = System.currentTimeMillis() / 1000; // epoch second presence.startTimestamp = System.currentTimeMillis() / 1000; // epoch second
presence.state = state; presence.state = state;
//presence.details = details; //presence.details = details;
presence.largeImageKey = "logo"; presence.largeImageKey = "logo";
presence.largeImageText = details; presence.largeImageText = details;
lib.Discord_UpdatePresence(presence); lib.Discord_UpdatePresence(presence);
} }
@Override @Override
public void onGameExit() { public void onGameExit() {
DiscordRPC.INSTANCE.Discord_Shutdown(); DiscordRPC.INSTANCE.Discord_Shutdown();
} }
}; };
Mindustry.args = Array.with(arg); Mindustry.args = Array.with(arg);
Log.set(Log.LEVEL_DEBUG); Net.setClientProvider(new KryoClient());
Net.setServerProvider(new KryoServer());
Net.setClientProvider(new ClientProvider() { try {
Client client; new Lwjgl3Application(new Mindustry(), config);
}catch (Exception e){
e.printStackTrace();
{ //don't create crash logs for me, as it's expected
client = new Client(); if(System.getProperty("user.name").equals("anuke")) return;
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);
}
@Override String result = Strings.parseException(e, true);
public void disconnected (Connection connection) { boolean failed = false;
Disconnect c = new Disconnect();
Net.handleClientReceived(c);
}
@Override String filename = "crash-report-" + DateFormat.getDateTimeInstance().format(new Date()) + ".txt";
public void received (Connection connection, Object object) {
if(object instanceof FrameworkMessage) return;
Net.handleClientReceived(object);
}
});
register(Registrator.getClasses()); try{
} Files.write(Paths.get(filename), result.getBytes());
}catch (IOException i){
i.printStackTrace();
failed = true;
}
@Override JOptionPane.showMessageDialog(null, "An error has occured: \n" + result + "\n\n" +
public void connect(String ip, int port) throws IOException { (!failed ? "A crash report has been written to " + new File(filename).getAbsolutePath() + ".\nPlease send this file to the developer!"
client.connect(5000, ip, port, port); : "Failed to generate crash report.\nPlease send an image of this crash log to the developer!"));
} }
@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);
} }
} }

4
kryonet/build.gradle Normal file
View File

@@ -0,0 +1,4 @@
apply plugin: "java"
sourceCompatibility = 1.8
sourceSets.main.java.srcDirs = [ "src/" ]

Binary file not shown.

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@@ -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<Address> discover(){
List<InetAddress> list = client.discoverHosts(Vars.port, 5000);
ObjectSet<String> hostnames = new ObjectSet<>();
Array<Address> 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);
}
}
}

View File

@@ -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()));
}
}

View File

@@ -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(System.properties["release"] == null || System.properties["release"].equals("false")){
if (new File(settingsDir, '../uCore').exists()) { if (new File(settingsDir, '../uCore').exists()) {