diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index e41cf25858..03bdb62bc8 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -217,7 +217,7 @@ setting.sensitivity.name=Controller Sensitivity setting.saveinterval.name=Autosave Interval setting.seconds={0} Seconds setting.fullscreen.name=Fullscreen -setting.multithread.name=Multithreading [scarlet](unstable!) +setting.multithread.name=Multithreading [scarlet](extremely unstable!) setting.fps.name=Show FPS setting.vsync.name=VSync setting.lasers.name=Show Power Lasers diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 851d1bc4b9..86795a8641 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -268,13 +268,6 @@ public class Control extends Module{ Entities.collisions().setCollider(tilesize, world::solid); Platform.instance.updateRPC(); - - //TODO remove - /* - Timers.runTask(2, () -> { - state.set(State.playing); - SaveIO.loadFromSlot(0); - });*/ } @Override diff --git a/core/src/io/anuke/mindustry/core/ThreadHandler.java b/core/src/io/anuke/mindustry/core/ThreadHandler.java index 7b1a5a0a52..885c48f99d 100644 --- a/core/src/io/anuke/mindustry/core/ThreadHandler.java +++ b/core/src/io/anuke/mindustry/core/ThreadHandler.java @@ -11,6 +11,7 @@ public class ThreadHandler { private final ThreadProvider impl; private float delta = 1f; private long frame = 0; + private float framesSinceUpdate; private boolean enabled; private final Object updateLock = new Object(); @@ -30,9 +31,15 @@ public class ThreadHandler { return frame; } + public float getFramesSinceUpdate(){ + return framesSinceUpdate; + } + public void handleRender(){ if(!enabled) return; + framesSinceUpdate += Timers.delta(); + synchronized (updateLock) { rendered = true; updateLock.notify(); @@ -64,7 +71,6 @@ public class ThreadHandler { while (true) { long time = TimeUtils.millis(); logic.update(); - //impl.sleep(130); long elapsed = TimeUtils.timeSinceMillis(time); long target = (long) (1000 / 60f); @@ -83,6 +89,7 @@ public class ThreadHandler { } frame ++; + framesSinceUpdate = 0; } } catch (InterruptedException ex) { Log.info("Stopping logic thread."); diff --git a/core/src/io/anuke/mindustry/entities/Bullet.java b/core/src/io/anuke/mindustry/entities/Bullet.java index 40d77f07f2..b1b0d4c9ec 100644 --- a/core/src/io/anuke/mindustry/entities/Bullet.java +++ b/core/src/io/anuke/mindustry/entities/Bullet.java @@ -9,21 +9,26 @@ import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.*; public class Bullet extends BulletEntity{ - public boolean absorbed = false; public Bullet(BulletType type, Entity owner, float x, float y, float angle){ super(type, owner, angle); set(x, y); this.type = type; } - - public void absorb(){ - absorbed = true; - remove(); - } public void draw(){ - type.draw(this); + //interpolate position linearly at low tick speeds + if(SyncEntity.isSmoothing()){ + x += threads.getFramesSinceUpdate() * velocity.x; + y += threads.getFramesSinceUpdate() * velocity.y; + + type.draw(this); + + x -= threads.getFramesSinceUpdate() * velocity.x; + y -= threads.getFramesSinceUpdate() * velocity.y; + }else{ + type.draw(this); + } } public float drawSize(){ diff --git a/core/src/io/anuke/mindustry/entities/Player.java b/core/src/io/anuke/mindustry/entities/Player.java index a54e1a6f74..305c70c297 100644 --- a/core/src/io/anuke/mindustry/entities/Player.java +++ b/core/src/io/anuke/mindustry/entities/Player.java @@ -2,7 +2,6 @@ package io.anuke.mindustry.entities; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.TimeUtils; import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Shaders; import io.anuke.mindustry.net.Net; @@ -275,32 +274,15 @@ public class Player extends SyncEntity{ this.health = health; this.dashing = dashing == 1; - interpolator.targetrot = angle; - interpolator.time = 0f; - interpolator.last.set(this.x, this.y); - interpolator.target.set(x, y); - interpolator.spacing = Math.min(Math.max(((TimeUtils.timeSinceMillis(time) / 1000f) * 60f), 4f), 10); + interpolator.read(this.x, this.y, x, y, angle, time); } @Override public void interpolate() { + super.interpolate(); + Interpolator i = interpolator; - i.time += 1f / i.spacing * Timers.delta(); - - Mathf.lerp2(movement.set(i.last), i.target, i.time); - - x = movement.x; - y = movement.y; - - if(i.target.dst(x, y) > 128){ - set(i.target.x, i.target.y); - i.time = 0f; - i.last.set(i.target); - } - - angle = Mathf.lerpAngDelta(angle, i.targetrot, 0.6f); - float tx = x + Angles.trnsx(angle + 180f, 3f); float ty = y + Angles.trnsy(angle + 180f, 3f); diff --git a/core/src/io/anuke/mindustry/entities/SyncEntity.java b/core/src/io/anuke/mindustry/entities/SyncEntity.java index 968bdf285d..7d29b4e9f0 100644 --- a/core/src/io/anuke/mindustry/entities/SyncEntity.java +++ b/core/src/io/anuke/mindustry/entities/SyncEntity.java @@ -2,8 +2,11 @@ package io.anuke.mindustry.entities; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.ObjectIntMap; +import com.badlogic.gdx.utils.TimeUtils; import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.ucore.core.Timers; import io.anuke.ucore.entities.DestructibleEntity; import io.anuke.ucore.util.Mathf; @@ -16,9 +19,8 @@ public abstract class SyncEntity extends DestructibleEntity{ protected transient Interpolator interpolator = new Interpolator(); - //for interpolating at low tick speeds. - private transient Vector2 tpos = new Vector2(-999, -999); - private transient float tang = 0f; + //smoothed position/angle + private Vector3 spos = new Vector3(); public float angle; @@ -27,12 +29,23 @@ public abstract class SyncEntity extends DestructibleEntity{ setWriteSize(Player.class, 4 + 4 + 4 + 2 + 1); } + public static boolean isSmoothing(){ + return threads.isEnabled() && threads.getFPS() <= Gdx.graphics.getFramesPerSecond() / 2f; + } + public abstract void writeSpawn(ByteBuffer data); public abstract void readSpawn(ByteBuffer data); public abstract void write(ByteBuffer data); public abstract void read(ByteBuffer data, long time); - public abstract void interpolate(); + + public void interpolate(){ + interpolator.update(); + + x = interpolator.pos.x; + y = interpolator.pos.y; + angle = interpolator.angle; + } @Override public final void draw(){ @@ -40,15 +53,13 @@ public abstract class SyncEntity extends DestructibleEntity{ //interpolates data at low tick speeds. if(isSmoothing()){ - if(tpos.dst(x, y) > 100){ - tpos.set(x, y); + if(Vector2.dst(spos.x, spos.y, x, y) > 128){ + spos.set(x, y, angle); } - tpos.x = Mathf.lerpDelta(tpos.x, x, 0.3f); - tpos.y = Mathf.lerpDelta(tpos.y, y, 0.3f); - tang = Mathf.lerpAngDelta(tang, angle, 0.3f); - this.x = tpos.x; - this.y = tpos.y; - this.angle = tang; + + this.x = spos.x = Mathf.lerpDelta(spos.x, x, 0.2f); + this.y = spos.y = Mathf.lerpDelta(spos.y, y, 0.2f); + this.angle = spos.z = Mathf.lerpAngDelta(spos.z, angle, 0.3f); } drawSmooth(); @@ -58,12 +69,8 @@ public abstract class SyncEntity extends DestructibleEntity{ this.angle = angle; } - private boolean isSmoothing(){ - return threads.isEnabled() && threads.getFPS() <= Gdx.graphics.getFramesPerSecond() / 2f; - } - - public Vector2 getDrawPosition(){ - return isSmoothing() ? tpos : tpos.set(x, y); + public Vector3 getDrawPosition(){ + return isSmoothing() ? spos : spos.set(x, y, angle); } public void drawSmooth(){} @@ -91,12 +98,40 @@ public abstract class SyncEntity extends DestructibleEntity{ return (T)this; } - public class Interpolator { + public static class Interpolator { + //used for movement public Vector2 target = new Vector2(); public Vector2 last = new Vector2(); - public Vector2 vec = new Vector2(); public float targetrot; public float spacing = 1f; public float time; + + //current state + public Vector2 pos = new Vector2(); + public float angle; + + public void read(float cx, float cy, float x, float y, float angle, long sent){ + targetrot = angle; + time = 0f; + last.set(cx, cy); + target.set(x, y); + spacing = Math.min(Math.max(((TimeUtils.timeSinceMillis(sent) / 1000f) * 60f), 4f), 10); + } + + public void update(){ + + time += 1f / spacing * Timers.delta(); + + Mathf.lerp2(pos.set(last), target, time); + + angle = Mathf.lerpAngDelta(angle, targetrot, 0.6f); + + if(target.dst(pos) > 128){ + pos.set(target); + last.set(target); + time = 0f; + } + + } } } diff --git a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java index 2ab55ea398..1c0a92d95e 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java @@ -2,17 +2,17 @@ package io.anuke.mindustry.entities.enemies; import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.TimeUtils; import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.NetEvents; -import io.anuke.ucore.core.Timers; import io.anuke.ucore.entities.Entity; import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.util.*; +import io.anuke.ucore.util.Mathf; +import io.anuke.ucore.util.Timer; +import io.anuke.ucore.util.Translator; import java.nio.ByteBuffer; @@ -141,25 +141,7 @@ public class Enemy extends SyncEntity { this.health = health; - interpolator.targetrot = angle / 2f; - interpolator.time = 0f; - interpolator.last.set(this.x, this.y); - interpolator.target.set(x, y); - interpolator.spacing = Math.min(Math.max(((TimeUtils.timeSinceMillis(time) / 1000f) * 60f), 4f), 10f); - } - - @Override - public void interpolate() { - Interpolator i = interpolator; - - i.time += 1f / i.spacing * Timers.delta(); - - Mathf.lerp2(i.vec.set(i.last), i.target, i.time); - - x = i.vec.x; - y = i.vec.y; - - angle = Mathf.lerpAngDelta(angle, i.targetrot, 0.6f); + interpolator.read(this.x, this.y, x, y, angle, time); } public void shoot(BulletType bullet){ diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index ba77d1a2bc..e0957fa0f6 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -25,7 +25,7 @@ import java.io.IOException; import static io.anuke.mindustry.Vars.*; public class Net{ - public static final int version = 19; + public static final int version = 20; private static boolean server; private static boolean active; diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 08646fea96..646478c94f 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -508,15 +508,15 @@ public class Packets { @Override public void write(ByteBuffer buffer) { - buffer.putInt(position); - buffer.put(rotation); + buffer.putInt((rotation) | (position << 2)); buffer.put(itemid); } @Override public void read(ByteBuffer buffer) { - position = buffer.getInt(); - rotation = buffer.get(); + int i = buffer.getInt(); + rotation = (byte)(i & 0x3); + position = i >> 2; itemid = buffer.get(); } } diff --git a/core/src/io/anuke/mindustry/world/blocks/types/LiquidBlock.java b/core/src/io/anuke/mindustry/world/blocks/types/LiquidBlock.java index a775431132..cca55a313a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/LiquidBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/LiquidBlock.java @@ -5,6 +5,7 @@ import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.resource.Liquid; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; +import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.util.Mathf; @@ -80,7 +81,7 @@ public class LiquidBlock extends Block implements LiquidAcceptor{ LiquidAcceptor other = (LiquidAcceptor)next.block(); float flow = Math.min(other.getLiquidCapacity(next) - other.getLiquid(next) - 0.001f, - Math.min(entity.liquidAmount/flowfactor, entity.liquidAmount)); + Math.min(entity.liquidAmount/flowfactor * Timers.delta(), entity.liquidAmount)); if(flow <= 0f || entity.liquidAmount < flow) return; 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 8ae8bd0061..ff286cea59 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 @@ -67,13 +67,13 @@ public class Conveyor extends Block{ } @Override - public synchronized void drawLayer(Tile tile){ + public void drawLayer(Tile tile){ ConveyorEntity entity = tile.entity(); byte rotation = tile.getRotation(); for(int i = 0; i < entity.convey.size; i ++){ - ItemPos pos = drawpos.set(entity.convey.get(i)); + ItemPos pos = drawpos.set(entity.convey.get(i), ItemPos.drawShorts); if(pos.item == null) continue; @@ -87,19 +87,16 @@ public class Conveyor extends Block{ } @Override - public synchronized void update(Tile tile){ + public void update(Tile tile){ ConveyorEntity entity = tile.entity(); entity.minitem = 1f; - float shift = entity.elapsed * speed; int minremove = Integer.MAX_VALUE; for(int i = 0; i < entity.convey.size; i ++){ long value = entity.convey.get(i); - ItemPos pos = pos1.set(value); - - pos.y += shift; + ItemPos pos = pos1.set(value, ItemPos.updateShorts); //..this should never happen, but in case it does, remove it and stop here if(pos.item == null){ @@ -107,7 +104,7 @@ public class Conveyor extends Block{ break; } - float nextpos = (i == entity.convey.size - 1 ? 100f : pos2.set(entity.convey.get(i + 1)).y) - itemSpace; + float nextpos = (i == entity.convey.size - 1 ? 100f : pos2.set(entity.convey.get(i + 1), ItemPos.updateShorts).y) - itemSpace; float maxmove = Math.min(nextpos - pos.y, speed * Timers.delta()); if(maxmove > minmove){ @@ -128,10 +125,8 @@ public class Conveyor extends Block{ entity.minitem = pos.y; entity.convey.set(i, value); } - } - entity.elapsed = 0f; if(minremove != Integer.MAX_VALUE) entity.convey.truncate(minremove); } @@ -187,7 +182,7 @@ public class Conveyor extends Block{ public static class ConveyorEntity extends TileEntity{ LongArray convey = new LongArray(); - float minitem = 1, elapsed; + float minitem = 1; @Override public void write(DataOutputStream stream) throws IOException{ @@ -210,12 +205,6 @@ 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(long[] elements, int length){ @@ -243,8 +232,8 @@ public class Conveyor extends Block{ } private static int compareItems(Long a, Long b){ - pos1.set(a); - pos2.set(b); + pos1.set(a, ItemPos.packShorts); + pos2.set(b, ItemPos.packShorts); return Float.compare(pos1.y, pos2.y); } @@ -253,14 +242,18 @@ public class Conveyor extends Block{ private static short[] writeShort = new short[4]; private static byte[] writeByte = new byte[4]; + private static short[] packShorts = new short[4]; + private static short[] drawShorts = new short[4]; + private static short[] updateShorts = new short[4]; + Item item; float x, y; byte seed; private ItemPos(){} - ItemPos set(long lvalue){ - short[] values = Bits.getShorts(lvalue); + ItemPos set(long lvalue, short[] values){ + Bits.getShorts(lvalue, values); if(values[0] >= Item.getAllItems().size || values[0] < 0) item = null; @@ -278,7 +271,7 @@ public class Conveyor extends Block{ } static long packItem(Item item, float x, float y, byte seed){ - short[] shorts = Bits.getShorts(); + short[] shorts = packShorts; shorts[0] = (short)item.id; shorts[1] = (short)(x*Short.MAX_VALUE); shorts[2] = (short)((y - 1f)*Short.MAX_VALUE); diff --git a/core/src/io/anuke/mindustry/world/blocks/types/distribution/Junction.java b/core/src/io/anuke/mindustry/world/blocks/types/distribution/Junction.java index 8242c3bd7b..9d2f2abfac 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/distribution/Junction.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/distribution/Junction.java @@ -22,18 +22,6 @@ public class Junction extends Block{ public boolean canReplace(Block other){ return other instanceof Conveyor || other instanceof Router; } - - @Override - public void handleItem(Item item, Tile tile, Tile source){ - JunctionEntity entity = tile.entity(); - boolean x = tile.x == source.x; - long value = Bits.packLong(NumberUtils.floatToIntBits(Timers.time()), Bits.packInt((short)item.id, source.relativeTo(tile.x, tile.y))); - if(x){ - entity.bx.add(value); - }else { - entity.by.add(value); - } - } @Override public void update(Tile tile){ @@ -64,6 +52,18 @@ public class Junction extends Block{ } } + @Override + public void handleItem(Item item, Tile tile, Tile source){ + JunctionEntity entity = tile.entity(); + boolean x = tile.x == source.x; + long value = Bits.packLong(NumberUtils.floatToIntBits(Timers.time()), Bits.packInt((short)item.id, source.relativeTo(tile.x, tile.y))); + if(x){ + entity.bx.add(value); + }else { + entity.by.add(value); + } + } + @Override public boolean acceptItem(Item item, Tile tile, Tile source){ JunctionEntity entity = tile.entity(); @@ -91,6 +91,7 @@ public class Junction extends Block{ int index; void add(long id){ + if(full()) return; items[index++] = id; }