Started working on discovery, implemented GWT saving

This commit is contained in:
Anuken
2018-01-01 21:33:51 -05:00
parent 1b55d00254
commit 24248df4af
13 changed files with 190 additions and 61 deletions

View File

@@ -185,6 +185,7 @@ 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.loadAll("io.anuke.moment"); Settings.loadAll("io.anuke.moment");

View File

@@ -35,6 +35,7 @@ public class NetClient extends Module {
boolean connecting = false; boolean connecting = false;
boolean gotEntities = false; boolean gotEntities = false;
float playerSyncTime = 2; float playerSyncTime = 2;
float dataTimeout = 60*10;
public NetClient(){ public NetClient(){
@@ -50,6 +51,14 @@ public class NetClient extends Module {
c.name = UCore.getProperty("user.name"); c.name = UCore.getProperty("user.name");
c.android = Vars.android; c.android = Vars.android;
Net.send(c, SendMode.tcp); 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 -> {
@@ -243,7 +252,7 @@ public class NetClient extends Module {
} }
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();

View File

@@ -2,6 +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.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;
@@ -16,6 +17,7 @@ import io.anuke.mindustry.world.Map;
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.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.io.*;
@@ -106,71 +108,71 @@ public class SaveIO{
}}; }};
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);
} }

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

@@ -26,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;
} }
} }
@@ -73,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(){
@@ -81,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;
@@ -89,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(){
@@ -114,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(){

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,9 +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.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;
@@ -22,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{
@@ -50,6 +53,18 @@ public class Net{
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 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.*/ /**Returns a list of all connections IDs.*/
public static IntArray getConnections(){ public static IntArray getConnections(){
return serverProvider.getConnections(); return serverProvider.getConnections();
@@ -152,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);
@@ -170,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.*/
@@ -194,6 +219,8 @@ public class Net{
public void register(Class<?>... types); public void register(Class<?>... types);
/**Returns the ping for a certain connection.*/ /**Returns the ping for a certain connection.*/
public int getPingFor(int connection); public int getPingFor(int connection);
/**Close all connections.*/
public void dispose();
} }
public enum SendMode{ public enum SendMode{

View File

@@ -0,0 +1,12 @@
package io.anuke.mindustry.ui;
public class JoinDialog extends FloatingDialog {
public JoinDialog(){
super("$text.joingame");
}
void setup(){
content().clear();
}
}

View File

@@ -44,7 +44,7 @@ public class MenuDialog extends FloatingDialog{
ui.showPrefs(); ui.showPrefs();
}); });
if(!Vars.gwt){ //if(!Vars.gwt){
content().row(); content().row();
content().addButton("$text.savegame", () -> { content().addButton("$text.savegame", () -> {
save.show(); save.show();
@@ -54,7 +54,7 @@ public class MenuDialog extends FloatingDialog{
content().addButton("$text.loadgame", () -> { content().addButton("$text.loadgame", () -> {
load.show(); load.show();
}); });
} //}
content().row(); content().row();

View File

@@ -34,10 +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();

View File

@@ -1,10 +1,13 @@
package io.anuke.kryonet; package io.anuke.kryonet;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Array;
import com.esotericsoftware.kryonet.Client; import com.esotericsoftware.kryonet.Client;
import com.esotericsoftware.kryonet.Connection; import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.FrameworkMessage; import com.esotericsoftware.kryonet.FrameworkMessage;
import com.esotericsoftware.kryonet.Listener; 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;
import io.anuke.mindustry.net.Net.ClientProvider; import io.anuke.mindustry.net.Net.ClientProvider;
import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Net.SendMode;
@@ -13,6 +16,8 @@ import io.anuke.mindustry.net.Packets.Disconnect;
import io.anuke.mindustry.net.Registrator; import io.anuke.mindustry.net.Registrator;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress;
import java.util.List;
public class KryoClient implements ClientProvider{ public class KryoClient implements ClientProvider{
Client client; Client client;
@@ -93,10 +98,32 @@ public class KryoClient implements ClientProvider{
return client.getReturnTripTime(); return client.getReturnTripTime();
} }
@Override
public Array<Address> discover(){
List<InetAddress> list = client.discoverHosts(Vars.port, 5000);
Array<Address> result = new Array<>();
for(InetAddress a : list){
result.add(new Address(a.getHostName(), a.getHostAddress()));
}
return result;
}
@Override @Override
public void register(Class<?>... types) { public void register(Class<?>... types) {
for(Class<?> c : types){ for(Class<?> c : types){
client.getKryo().register(c); client.getKryo().register(c);
} }
} }
@Override
public void dispose(){
try {
client.dispose();
}catch (IOException e){
throw new RuntimeException(e);
}
}
} }

View File

@@ -159,6 +159,15 @@ public class KryoServer implements ServerProvider {
} }
} }
@Override
public void dispose(){
try {
server.dispose();
}catch (IOException e){
throw new RuntimeException(e);
}
}
Connection getByID(int id){ Connection getByID(int id){
for(Connection con : server.getConnections()){ for(Connection con : server.getConnections()){
if(con.getID() == id){ if(con.getID() == id){