diff --git a/build.gradle b/build.gradle index 6f767c8370..095b0e129a 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ allprojects { appName = "Mindustry" gdxVersion = '1.9.8' aiVersion = '1.8.1' - uCoreVersion = '46c6564'; + uCoreVersion = '89fa665'; } repositories { diff --git a/core/assets-raw/sprites/icon-iron.png b/core/assets-raw/sprites/icon-iron.png index f9072d1348..50f34510ae 100644 Binary files a/core/assets-raw/sprites/icon-iron.png and b/core/assets-raw/sprites/icon-iron.png differ diff --git a/core/assets/sprites/sprites.png b/core/assets/sprites/sprites.png index 54ab64069c..33fc2eeb0c 100644 Binary files a/core/assets/sprites/sprites.png and b/core/assets/sprites/sprites.png differ diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index eabc0b3c67..237f1a16f4 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -18,6 +18,8 @@ import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Syncable; import io.anuke.mindustry.net.Syncable.Interpolator; +import io.anuke.mindustry.resource.Recipe; +import io.anuke.mindustry.resource.Recipes; import io.anuke.mindustry.resource.Upgrade; import io.anuke.mindustry.resource.Weapon; import io.anuke.mindustry.world.Block; @@ -125,7 +127,6 @@ public class NetClient extends Module { Syncable sync = ((Syncable)entity); if(sync == null){ - Gdx.app.error("Mindustry", "Unknown entity ID: " + id + " " + (i >= packet.enemyStart ? "(enemy)" : "(player)")); if(!requests.contains(id)){ requests.add(id); Gdx.app.error("Mindustry", "Sending entity request: " + id); @@ -150,7 +151,11 @@ public class NetClient extends Module { }); Net.handle(PlacePacket.class, packet -> { - Gdx.app.postRunnable(() -> Vars.control.input.placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false)); + Gdx.app.postRunnable(() ->{ + Recipe recipe = Recipes.getByResult(Block.getByID(packet.block)); + if(recipe != null) Vars.control.removeItems(recipe.requirements); + Vars.control.input.placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false); + }); }); Net.handle(BreakPacket.class, packet -> { @@ -229,8 +234,9 @@ public class NetClient extends Module { Gdx.app.postRunnable(() -> { try { - long timestamp = stream.readLong(); - float elapsed = TimeUtils.timeSinceMillis(timestamp) / 1000f * 60f; + + float time = stream.readFloat(); + float elapsed = Timers.time() - time; while (stream.available() > 0) { int pos = stream.readInt(); @@ -241,10 +247,13 @@ public class NetClient extends Module { byte times = stream.readByte(); for (int i = 0; i < times; i++) { - tile.entity.timer.getTimes()[i] = stream.readFloat() + elapsed; + tile.entity.timer.getTimes()[i] = stream.readFloat(); } - tile.entity.read(stream); + short data = stream.readShort(); + tile.setPackedData(data); + + tile.entity.readNetwork(stream, elapsed); } } catch (IOException e) { throw new RuntimeException(e); @@ -325,6 +334,10 @@ public class NetClient extends Module { } } + public void beginConnecting(){ + connecting = true; + } + public void disconnectQuietly(){ kicked = true; Net.disconnect(); diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 436577b14b..b2c982a908 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -336,7 +336,7 @@ public class NetServer extends Module{ try { DataOutputStream stream = new DataOutputStream(bs); - stream.writeLong(TimeUtils.millis()); + stream.writeFloat(Timers.time()); for (int rx = -viewx / 2; rx <= viewx / 2; rx++) { for (int ry = -viewy / 2; ry <= viewy / 2; ry++) { @@ -359,6 +359,8 @@ public class NetServer extends Module{ stream.writeFloat(tile.entity.timer.getTimes()[i]); } + stream.writeShort(tile.getPackedData()); + tile.entity.write(stream); } } diff --git a/core/src/io/anuke/mindustry/entities/TileEntity.java b/core/src/io/anuke/mindustry/entities/TileEntity.java index 21ea1d52b1..9d755b3116 100644 --- a/core/src/io/anuke/mindustry/entities/TileEntity.java +++ b/core/src/io/anuke/mindustry/entities/TileEntity.java @@ -53,6 +53,10 @@ public class TileEntity extends Entity{ public void read(DataInputStream stream) throws IOException{ } + + public void readNetwork(DataInputStream stream, float elapsed) throws IOException{ + read(stream); + } public void onDeath(){ onDeath(false); diff --git a/core/src/io/anuke/mindustry/io/NetworkIO.java b/core/src/io/anuke/mindustry/io/NetworkIO.java index f2e097370a..b4bffa9650 100644 --- a/core/src/io/anuke/mindustry/io/NetworkIO.java +++ b/core/src/io/anuke/mindustry/io/NetworkIO.java @@ -19,7 +19,7 @@ import io.anuke.ucore.entities.Entities; import java.io.*; public class NetworkIO { - private static final int fileVersionID = 14; + private static final int fileVersionID = 15; public static void write(int playerID, ByteArray upgrades, OutputStream os){ @@ -106,7 +106,7 @@ public class NetworkIO { } if(tile.entity != null){ - stream.writeByte(tile.getRotation()); //placerot + stream.writeShort(tile.getPackedData()); stream.writeShort(tile.entity.health); //health //items @@ -240,11 +240,11 @@ public class NetworkIO { } if(tile.entity != null){ - byte rotation = stream.readByte(); + short data = stream.readShort(); short health = stream.readShort(); tile.entity.health = health; - tile.setRotation(rotation); + tile.setPackedData(data); for(int j = 0; j < tile.entity.items.length; j ++){ tile.entity.items[j] = stream.readInt(); diff --git a/core/src/io/anuke/mindustry/mapeditor/MapGenerateDialog.java b/core/src/io/anuke/mindustry/mapeditor/MapGenerateDialog.java index 430f5fd52f..330f70b7ee 100644 --- a/core/src/io/anuke/mindustry/mapeditor/MapGenerateDialog.java +++ b/core/src/io/anuke/mindustry/mapeditor/MapGenerateDialog.java @@ -24,7 +24,7 @@ public class MapGenerateDialog extends FloatingDialog{ private boolean loading; public MapGenerateDialog(MapEditor editor) { - super("$text.generate"); + super("$text.editor.generate"); this.editor = editor; Stack stack = new Stack(); diff --git a/core/src/io/anuke/mindustry/mapeditor/MapView.java b/core/src/io/anuke/mindustry/mapeditor/MapView.java index 92abf3543e..0cc7ddabc1 100644 --- a/core/src/io/anuke/mindustry/mapeditor/MapView.java +++ b/core/src/io/anuke/mindustry/mapeditor/MapView.java @@ -143,7 +143,7 @@ public class MapView extends Element implements GestureListener{ if(op == null) op = new DrawOperation(editor.pixmap()); Pixmap next = Pixmaps.copy(editor.pixmap()); op.add(current, next); - current = next; + current = null; stack.add(op); op = null; } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java index 83c600df15..1268a261d1 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java @@ -214,6 +214,7 @@ public class JoinDialog extends FloatingDialog { Timers.runTask(2f, () -> { try{ + Vars.netClient.beginConnecting(); Net.connect(ip, port); hide(); join.hide(); diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index 57fdbd9e2b..235f3f31f0 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -9,6 +9,7 @@ import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.net.Net; import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Settings; +import io.anuke.ucore.core.Timers; import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.label; @@ -155,6 +156,8 @@ public class HudFragment implements Fragment{ row(); new label(() -> "[orange]noclip: " + Vars.noclip).left(); row(); + new label(() -> "[purple]time: " + (int)(Timers.time() / 10f) % 50).left(); + row(); new label("[red]DEBUG MODE").scale(0.5f).left(); }}.end(); } diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index f05756cc06..b44f173cef 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -186,7 +186,7 @@ public class Block{ i++; i %= 4; } - tile.setDump((byte)pdump); + tile.setDump(pdump); handleItem(item, tile, tile); } diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index e2c7d49af6..89326ca590 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -154,6 +154,14 @@ public class Tile{ return Bits.getRightByte(Bits.getRightByte(data)); } + public short getPackedData(){ + return data; + } + + public void setPackedData(short data){ + this.data = data; + } + public boolean passable(){ Block block = block(); Block floor = floor(); diff --git a/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java b/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java index 0b4648fa03..fb6c983217 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/distribution/Conveyor.java @@ -92,6 +92,8 @@ public class Conveyor extends Block{ removals.clear(); + float shift = entity.elapsed * speed; + for(int i = 0; i < entity.convey.size; i ++){ int value = entity.convey.get(i); ItemPos pos = pos1.set(value); @@ -100,7 +102,7 @@ public class Conveyor extends Block{ !(pos2.set(entity.convey.get(i + 1)).y - pos.y < itemSpace * Timers.delta()); if(canmove){ - pos.y += Math.max(speed * Timers.delta(), 1f/252f); //TODO fix precision issues when at high FPS? + pos.y += Math.max(speed * Timers.delta() + shift, 1f/252f); //TODO fix precision issues when at high FPS? pos.x = Mathf.lerpDelta(pos.x, 0, 0.06f); }else{ pos.x = Mathf.lerpDelta(pos.x, pos.seed/offsetScl, 0.1f); @@ -119,6 +121,8 @@ public class Conveyor extends Block{ } } + + entity.elapsed = 0f; entity.convey.removeAll(removals); } @@ -174,7 +178,7 @@ public class Conveyor extends Block{ */ public static class ConveyorEntity extends TileEntity{ IntArray convey = new IntArray(); - float minitem = 1; + float minitem = 1, elapsed; @Override public void write(DataOutputStream stream) throws IOException{ @@ -197,6 +201,12 @@ public class Conveyor extends Block{ sort(convey.items, convey.size); } + + @Override + public void readNetwork(DataInputStream stream, float elapsed) throws IOException{ + read(stream); + this.elapsed = elapsed; + } } private static void sort(int[] elements, int length){ diff --git a/kryonet/src/io/anuke/kryonet/KryoClient.java b/kryonet/src/io/anuke/kryonet/KryoClient.java index cb40be6af1..c429873950 100644 --- a/kryonet/src/io/anuke/kryonet/KryoClient.java +++ b/kryonet/src/io/anuke/kryonet/KryoClient.java @@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectSet; import com.esotericsoftware.kryonet.*; import com.esotericsoftware.kryonet.FrameworkMessage.DiscoverHost; +import com.esotericsoftware.kryonet.Listener.LagListener; import com.esotericsoftware.kryonet.serialization.Serialization; import io.anuke.mindustry.Vars; import io.anuke.mindustry.net.Host; @@ -51,15 +52,15 @@ public class KryoClient implements ClientProvider{ } }; - client = new Client(); + client = new Client(8192, 2048*2); client.setDiscoveryHandler(handler); - client.addListener(new Listener(){ + Listener listener = new Listener(){ @Override public void connected (Connection connection) { Connect c = new Connect(); c.id = connection.getID(); - c.addressTCP = connection.getRemoteAddressTCP().toString(); + if(connection.getRemoteAddressTCP() != null) c.addressTCP = connection.getRemoteAddressTCP().toString(); try{ Net.handleClientReceived(c); @@ -95,9 +96,14 @@ public class KryoClient implements ClientProvider{ }); } } - } - }); + }; + + if(KryoRegistrator.fakeLag){ + client.addListener(new LagListener(0, KryoRegistrator.fakeLagAmount, listener)); + }else{ + client.addListener(listener); + } register(Registrator.getClasses()); } diff --git a/kryonet/src/io/anuke/kryonet/KryoRegistrator.java b/kryonet/src/io/anuke/kryonet/KryoRegistrator.java index a104347e3e..15aebd8ae3 100644 --- a/kryonet/src/io/anuke/kryonet/KryoRegistrator.java +++ b/kryonet/src/io/anuke/kryonet/KryoRegistrator.java @@ -9,9 +9,12 @@ import java.net.InetAddress; import java.nio.ByteBuffer; public class KryoRegistrator { + public static boolean fakeLag = true; + public static final int fakeLagAmount = 500; public static void register(Kryo kryo){ //TODO register stuff? + //Log.set(Log.LEVEL_DEBUG); } public static ByteBuffer writeServerData(){ diff --git a/kryonet/src/io/anuke/kryonet/KryoServer.java b/kryonet/src/io/anuke/kryonet/KryoServer.java index e4640427af..b01b3c4e74 100644 --- a/kryonet/src/io/anuke/kryonet/KryoServer.java +++ b/kryonet/src/io/anuke/kryonet/KryoServer.java @@ -5,6 +5,7 @@ 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.Listener.LagListener; import com.esotericsoftware.kryonet.Server; import com.esotericsoftware.kryonet.util.InputStreamSender; import io.anuke.mindustry.net.Net; @@ -23,14 +24,13 @@ import io.anuke.ucore.core.Timers; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.Arrays; public class KryoServer implements ServerProvider { Server server; IntArray connections = new IntArray(); public KryoServer(){ - server = new Server(4096*2, 2048); //TODO tweak + server = new Server(4096*2, 2048*2); //TODO tweak server.setDiscoveryHandler((datagramChannel, fromAddress) -> { ByteBuffer buffer = KryoRegistrator.writeServerData(); UCore.log("Replying to discover request with buffer of size " + buffer.capacity()); @@ -39,7 +39,7 @@ public class KryoServer implements ServerProvider { return true; }); - server.addListener(new Listener(){ + Listener listener = new Listener(){ @Override public void connected (Connection connection) { @@ -81,7 +81,13 @@ public class KryoServer implements ServerProvider { //Gdx.app.postRunnable(() -> {throw new RuntimeException(e);}); } } - }); + }; + + if(KryoRegistrator.fakeLag){ + server.addListener(new LagListener(0, KryoRegistrator.fakeLagAmount, listener)); + }else{ + server.addListener(listener); + } register(Registrator.getClasses()); } @@ -93,14 +99,13 @@ public class KryoServer implements ServerProvider { @Override public void kick(int connection) { - Connection conn; - try { - conn = getByID(connection); - }catch (Exception e){ - e.printStackTrace(); + Connection conn = getByID(connection); + + if(conn == null){ connections.removeValue(connection); return; } + KickPacket p = new KickPacket(); p.reason = (byte)KickReason.kick.ordinal(); @@ -137,6 +142,7 @@ public class KryoServer implements ServerProvider { @Override public void sendStream(int id, Streamable stream) { Connection connection = getByID(id); + if(connection == null) return; connection.addListener(new InputStreamSender(stream.stream, 512) { int id; @@ -219,7 +225,6 @@ public class KryoServer implements ServerProvider { } } - throw new RuntimeException("Unable to find connection with ID " + id + "! Current connections: " - + Arrays.toString(server.getConnections())); + return null; } }