Autogenerated interpolation

This commit is contained in:
Anuken
2020-05-24 14:00:53 -04:00
parent 1acb5fc56c
commit 7c06ba94c1
36 changed files with 252 additions and 183 deletions

View File

@@ -48,7 +48,6 @@ public class UnitTypes implements ContentList{
dagger = new UnitType("dagger"){{
speed = 0.5f;
hitsize = 8f;
mass = 1.75f;
health = 130;
weapons.add(new Weapon("large-weapon"){{
reload = 14f;
@@ -64,7 +63,6 @@ public class UnitTypes implements ContentList{
tier = 2;
speed = 0.4f;
mass = 3.5f;
hitsize = 9f;
range = 10f;
health = 460;
@@ -85,7 +83,6 @@ public class UnitTypes implements ContentList{
crawler = new UnitType("crawler"){{
speed = 0.65f;
hitsize = 8f;
mass = 1.75f;
health = 120;
sway = 0.25f;
weapons.add(new Weapon(){{
@@ -160,7 +157,6 @@ public class UnitTypes implements ContentList{
tier = 3;
speed = 0.38f;
mass = 5f;
hitsize = 13f;
rotateSpeed = 3f;
targetAir = false;
@@ -184,7 +180,6 @@ public class UnitTypes implements ContentList{
speed = 0.4f;
drag = 0.4f;
mass = 5f;
hitsize = 10f;
rotateSpeed = 3f;
targetAir = false;
@@ -206,7 +201,6 @@ public class UnitTypes implements ContentList{
speed = 3f;
accel = 0.08f;
drag = 0.01f;
mass = 1.5f;
flying = true;
health = 75;
faceTarget = false;
@@ -229,7 +223,6 @@ public class UnitTypes implements ContentList{
health = 220;
speed = 2f;
mass = 3f;
accel = 0.08f;
drag = 0.016f;
flying = true;
@@ -255,7 +248,6 @@ public class UnitTypes implements ContentList{
speed = 1.1f;
accel = 0.02f;
drag = 0.05f;
mass = 30f;
rotateSpeed = 0.5f;
flying = true;
lowAltitude = true;
@@ -279,7 +271,6 @@ public class UnitTypes implements ContentList{
speed = 1.3f;
drag = 0.1f;
hitsize = 8f;
mass = 1.75f;
health = 130;
immunities = ObjectSet.with(StatusEffects.wet);
weapons.add(new Weapon("mount-weapon"){{
@@ -331,7 +322,6 @@ public class UnitTypes implements ContentList{
mineSpeed = 2f;
buildSpeed = 0.5f;
drag = 0.05f;
mass = 2f;
speed = 2.4f;
rotateSpeed = 15f;
accel = 0.1f;
@@ -356,7 +346,6 @@ public class UnitTypes implements ContentList{
phantom = new UnitType("phantom"){{
flying = true;
drag = 0.05f;
mass = 2f;
speed = 3f;
rotateSpeed = 15f;
accel = 0.3f;
@@ -374,10 +363,8 @@ public class UnitTypes implements ContentList{
drillTier = -1;
speed = 0.6f;
hitsize = 9f;
mass = 1.75f;
boostMultiplier = 2f;
itemCapacity = 15;
mass = 0.9f;
health = 160f;
buildSpeed = 0.9f;
canBoost = true;

View File

@@ -399,11 +399,11 @@ public class NetClient implements ApplicationListener{
}
//read the entity
entity.read(Reads.get(input));
entity.readSync(Reads.get(input));
if(created && entity.interpolator().target != null){
//set initial starting position
entity.setNet(entity.interpolator().target.x, entity.interpolator().target.y);
if(created){
//snap initial starting position
entity.snapSync();
}
if(add){

View File

@@ -36,6 +36,7 @@ import static mindustry.Vars.*;
public class NetServer implements ApplicationListener{
private final static int maxSnapshotSize = 430, timerBlockSync = 0;
private final static float serverSyncTime = 12, blockSyncTime = 60 * 8;
private final static FloatBuffer fbuffer = FloatBuffer.allocate(20);
private final static Vec2 vector = new Vec2();
private final static Rect viewport = new Rect();
/** If a player goes away of their server-side coordinates by this distance, they get teleported back. */
@@ -598,34 +599,53 @@ public class NetServer implements ApplicationListener{
float maxSpeed = player.dead() ? Float.MAX_VALUE : player.unit().type().speed;
float maxMove = elapsed / 1000f * 60f * maxSpeed * 1.1f;
vector.set(x - unit.interpolator().target.x, y - unit.interpolator().target.y);
if(connection.lastUnit != unit && !player.dead()){
connection.lastUnit = unit;
connection.lastPosition.set(unit);
}
vector.set(x, y).sub(connection.lastPosition);
vector.limit(maxMove);
float prevx = unit.x(), prevy = unit.y();
unit.set(unit.interpolator().target.x, unit.interpolator().target.y);
unit.set(connection.lastPosition);
if(!unit.isFlying()){
unit.move(vector.x, vector.y);
}else{
unit.trns(vector.x, vector.y);
}
//set last position after movement
connection.lastPosition.set(unit);
float newx = unit.x(), newy = unit.y();
if(!verifyPosition){
unit.x(prevx);
unit.y(prevy);
unit.set(prevx, prevy);
newx = x;
newy = y;
}else if(Mathf.dst(x, y, newx, newy) > correctDist){
}else if(!Mathf.within(x, y, newx, newy, correctDist)){
Call.onPositionSet(player.con(), newx, newy); //teleport and correct position when necessary
}
//reset player to previous synced position so it gets interpolated
unit.x(prevx);
unit.y(prevy);
unit.set(prevx, prevy);
//set interpolator target to *new* position so it moves toward it
unit.interpolator().read(unit.x(), unit.y(), newx, newy, rotation, baseRotation);
unit.vel().set(xVelocity, yVelocity); //only for visual calculation purposes, doesn't actually update the player
//write sync data to the buffer
fbuffer.limit(20);
fbuffer.position(0);
//now, put the new position, rotation and baserotation into the buffer so it can be read
if(unit instanceof Legsc) fbuffer.put(baseRotation); //base rotation is optional
fbuffer.put(rotation); //rotation is always there
fbuffer.put(newx);
fbuffer.put(newy);
fbuffer.flip();
//read sync data so it can be used for interpolation for the server
unit.readSyncManual(fbuffer);
//TODO fix
unit.vel().set(xVelocity, yVelocity); //only for visual calculation purposes, doesn't actually update the player (TODO or does it?)
}else{
player.x(x);
player.y(y);
@@ -795,7 +815,7 @@ public class NetServer implements ApplicationListener{
//write all entities now
dataStream.writeInt(entity.id()); //write id
dataStream.writeByte(entity.classId()); //write type ID
entity.write(Writes.get(dataStream)); //write entity
entity.writeSync(Writes.get(dataStream)); //write entity
sent++;

View File

@@ -7,9 +7,7 @@ import mindustry.gen.*;
@Component
abstract class LegsComp implements Posc, Flyingc, Hitboxc, Unitc, Legsc, ElevationMovec{
@Import float x, y;
float baseRotation;
@SyncField(false) float baseRotation;
transient float walkTime;
@Override

View File

@@ -1,5 +1,6 @@
package mindustry.entities.comp;
import arc.math.*;
import mindustry.annotations.Annotations.*;
import mindustry.async.PhysicsProcess.*;
import mindustry.gen.*;
@@ -9,10 +10,17 @@ import mindustry.gen.*;
* Has mass.*/
@Component
abstract class PhysicsComp implements Velc, Hitboxc, Flyingc{
transient PhysicRef physref;
transient float mass = 1f;
@Import float hitSize;
public void impulse(float x, float y){
transient PhysicRef physref;
//mass is simply the area of this object
float mass(){
return hitSize * hitSize * Mathf.pi;
}
void impulse(float x, float y){
float mass = mass();
vel().add(x / mass, y / mass);
}
}

View File

@@ -12,7 +12,7 @@ import static mindustry.Vars.world;
@Component
abstract class PosComp implements Position{
float x, y;
@SyncField(true) float x, y;
void set(float x, float y){
this.x = x;

View File

@@ -5,13 +5,5 @@ import mindustry.gen.*;
@Component
abstract class RotComp implements Entityc{
float rotation;
void interpolate(){
Syncc sync = as(Syncc.class);
if(sync.interpolator().values.length > 0){
rotation = sync.interpolator().values[0];
}
}
@SyncField(false) float rotation;
}

View File

@@ -1,26 +1,23 @@
package mindustry.entities.comp;
import arc.util.io.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
import mindustry.net.*;
import java.nio.*;
@Component
abstract class SyncComp implements Posc{
@Import float x, y;
abstract class SyncComp implements Entityc{
transient long lastUpdated, updateSpacing;
transient Interpolator interpolator = new Interpolator();
void setNet(float x, float y){
set(x, y);
//TODO change interpolator API
interpolator.target.set(x, y);
interpolator.last.set(x, y);
interpolator.pos.set(0, 0);
interpolator.updateSpacing = 16;
interpolator.lastUpdated = 0;
}
//all these method bodies are internally generated
void snapSync(){}
void readSync(Reads read){}
void writeSync(Writes write){}
void readSyncManual(FloatBuffer buffer){}
void writeSyncManual(FloatBuffer buffer){}
void interpolate(){}
@Override
public void update(){
@@ -28,10 +25,4 @@ abstract class SyncComp implements Posc{
interpolate();
}
}
void interpolate(){
interpolator.update();
x = interpolator.pos.x;
y = interpolator.pos.y;
}
}

View File

@@ -18,7 +18,7 @@ import static mindustry.Vars.*;
/** Stores player unlocks. Clientside only. */
public class GlobalData{
private ObjectMap<ContentType, ObjectSet<String>> unlocked = new ObjectMap<>();
private ObjectSet<String> unlocked = new ObjectSet<>();
private ObjectIntMap<Item> items = new ObjectIntMap<>();
private boolean modified;
@@ -128,7 +128,7 @@ public class GlobalData{
/** Returns whether or not this piece of content is unlocked yet. */
public boolean isUnlocked(UnlockableContent content){
return content.alwaysUnlocked() || unlocked.getOr(content.getContentType(), ObjectSet::new).contains(content.name);
return content.alwaysUnlocked() || unlocked.contains(content.name);
}
/**
@@ -140,7 +140,7 @@ public class GlobalData{
if(content.alwaysUnlocked()) return;
//fire unlock event so other classes can use it
if(unlocked.getOr(content.getContentType(), ObjectSet::new).add(content.name)){
if(unlocked.add(content.name)){
modified = true;
content.onUnlock();
Events.fire(new UnlockEvent(content));
@@ -162,7 +162,7 @@ public class GlobalData{
@SuppressWarnings("unchecked")
public void load(){
items.clear();
unlocked = Core.settings.getJson("unlocks", ObjectMap.class, ObjectMap::new);
unlocked = Core.settings.getJson("unlocked-content", ObjectSet.class, ObjectSet::new);
for(Item item : Vars.content.items()){
items.put(item, Core.settings.getInt("item-" + item.name, 0));
}
@@ -174,7 +174,7 @@ public class GlobalData{
}
public void save(){
Core.settings.putJson("unlocks", unlocked);
Core.settings.putJson("unlocked-content", String.class, unlocked);
for(Item item : Vars.content.items()){
Core.settings.put("item-" + item.name, items.get(item, 0));
}

View File

@@ -209,6 +209,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(controlledType != null && player.dead()){
Unitc unit = Units.closest(player.team(), player.x(), player.y(), u -> !u.isPlayer() && u.type() == controlledType);
if(unit != null){
Call.onUnitControl(player, unit);
}
@@ -218,6 +219,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public void checkUnit(){
if(controlledType != null){
Unitc unit = Units.closest(player.team(), player.x(), player.y(), u -> !u.isPlayer() && u.type() == controlledType);
if(unit == null && controlledType == UnitTypes.block){
unit = world.entWorld(player.x(), player.y()) instanceof ControlBlock ? ((ControlBlock)world.entWorld(player.x(), player.y())).unit() : null;
}
if(unit != null){
if(net.client()){
Call.onUnitControl(player, unit);

View File

@@ -1,66 +0,0 @@
package mindustry.net;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
public class Interpolator{
//used for movement
public Vec2 target = new Vec2();
public Vec2 last = new Vec2();
public float[] targets = {};
public float[] lasts = {};
public long lastUpdated, updateSpacing;
//current state
public Vec2 pos = new Vec2();
public float[] values = {};
public void read(float cx, float cy, float x, float y, float... target1ds){
if(lastUpdated != 0) updateSpacing = Time.timeSinceMillis(lastUpdated);
lastUpdated = Time.millis();
targets = target1ds;
if(lasts.length != values.length){
lasts = new float[values.length];
}
System.arraycopy(values, 0, lasts, 0, values.length);
last.set(cx, cy);
target.set(x, y);
}
public void reset(){
values = new float[0];
targets = new float[0];
target.setZero();
last.setZero();
lastUpdated = 0;
updateSpacing = 16; //1 frame
pos.setZero();
}
public void update(){
if(lastUpdated != 0 && updateSpacing != 0){
float timeSinceUpdate = Time.timeSinceMillis(lastUpdated);
float alpha = Math.min(timeSinceUpdate / updateSpacing, 2f);
pos.set(last).lerpPast(target, alpha);
if(values.length != targets.length){
values = new float[targets.length];
}
if(lasts.length != targets.length){
lasts = new float[targets.length];
}
for(int i = 0; i < values.length; i++){
values[i] = Mathf.slerp(lasts[i], targets[i], alpha);
}
}else{
pos.set(target);
}
}
}

View File

@@ -1,5 +1,6 @@
package mindustry.net;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
@@ -18,6 +19,8 @@ public abstract class NetConnection{
public String uuid = "AAAAAAAA", usid = uuid;
public boolean mobile, modclient;
public @Nullable Playerc player;
public @Nullable Unitc lastUnit;
public Vec2 lastPosition = new Vec2();
/** ID of last recieved client snapshot. */
public int lastRecievedClientSnapshot = -1;

View File

@@ -33,7 +33,7 @@ public class UnitType extends UnlockableContent{
public @Nullable UnitType upgrade;
public int tier = 1;
public float speed = 1.1f, boostMultiplier = 1f, rotateSpeed = 5f, baseRotateSpeed = 5f;
public float drag = 0.3f, mass = 1f, accel = 0.5f, landShake = 0f;
public float drag = 0.3f, accel = 0.5f, landShake = 0f;
public float health = 200f, range = -1, armor = 0f;
public boolean targetAir = true, targetGround = true;
public boolean faceTarget = true, isCounted = true, lowAltitude = false;

View File

@@ -84,8 +84,6 @@ public class OverflowGate extends Block{
lastItem = item;
time = 0f;
lastInput = source.tile();
updateTile();
}
public @Nullable Tilec getTileTarget(Item item, Tile src, boolean flip){

View File

@@ -43,7 +43,7 @@ public class UnitBlock extends PayloadAcceptor{
Unitc unit = payload.unit;
unit.set(x, y);
unit.rotation(rotdeg());
unit.vel().trns(rotdeg(), payloadSpeed * 2f).add(Mathf.range(0.1f), Mathf.range(0.1f));
unit.vel().trns(rotdeg(), payloadSpeed * 2f).add(Mathf.range(0.3f), Mathf.range(0.3f));
unit.trns(Tmp.v1.trns(rotdeg(), size * tilesize/2f));
unit.trns(unit.vel());
unit.add();

View File

@@ -35,8 +35,8 @@ public class UnitFactory extends UnitBlock{
hasPower = true;
hasItems = true;
solid = true;
flags = EnumSet.of(BlockFlag.producer, BlockFlag.unitModifier);
unitCapModifier = 4;
//flags = EnumSet.of(BlockFlag.producer, BlockFlag.unitModifier);
//unitCapModifier = 2;
configurable = true;
outputsPayload = true;
rotate = true;