Implemented authoritative movement/shooting, fixed netbugs

This commit is contained in:
Anuken
2018-06-13 17:06:46 -04:00
parent 4ca9a26bdd
commit c077f6e1e8
17 changed files with 268 additions and 87 deletions

View File

@@ -14,10 +14,14 @@ public class Mechs implements ContentList {
standard = new Mech("standard-mech", false){{ standard = new Mech("standard-mech", false){{
drillPower = 1; drillPower = 1;
speed = 1.1f;
maxSpeed = 1.1f;
}}; }};
standardShip = new Mech("standard-ship", true){{ standardShip = new Mech("standard-ship", true){{
drillPower = 1; drillPower = 1;
speed = 0.4f;
maxSpeed = 3f;
}}; }};
} }

View File

@@ -33,7 +33,7 @@ public class NetClient extends Module {
private final static float playerSyncTime = 2; private final static float playerSyncTime = 2;
private Timer timer = new Timer(5); private Timer timer = new Timer(5);
/**Whether the client is currently conencting.*/ /**Whether the client is currently connecting.*/
private boolean connecting = false; private boolean connecting = false;
/**If true, no message will be shown on disconnect.*/ /**If true, no message will be shown on disconnect.*/
private boolean quiet = false; private boolean quiet = false;
@@ -178,6 +178,12 @@ public class NetClient extends Module {
ui.loadfrag.hide(); ui.loadfrag.hide();
} }
@Remote(variants = Variant.one)
public static void onPositionSet(float x, float y){
players[0].x = x;
players[0].y = y;
}
@Remote(variants = Variant.one, unreliable = true) @Remote(variants = Variant.one, unreliable = true)
public static void onSnapshot(byte[] snapshot, int snapshotID){ public static void onSnapshot(byte[] snapshot, int snapshotID){
//skip snapshot IDs that have already been recieved //skip snapshot IDs that have already been recieved

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.core;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Colors; import com.badlogic.gdx.graphics.Colors;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Base64Coder; import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.IntMap;
@@ -40,6 +41,9 @@ import static io.anuke.mindustry.Vars.*;
public class NetServer extends Module{ public class NetServer extends Module{
private final static float serverSyncTime = 4, kickDuration = 30 * 1000; private final static float serverSyncTime = 4, kickDuration = 30 * 1000;
private final static boolean preventDuplicatNames = false; private final static boolean preventDuplicatNames = false;
private final static Vector2 vector = new Vector2();
/**If a play goes away of their server-side coordinates by this distance, they get teleported back.*/
private final static float correctDist = 16f;
public final Administration admins = new Administration(); public final Administration admins = new Administration();
@@ -153,10 +157,46 @@ public class NetServer extends Module{
NetConnection connection = Net.getConnection(id); NetConnection connection = Net.getConnection(id);
if(player == null || connection == null || packet.snapid < connection.lastRecievedSnapshot) return; if(player == null || connection == null || packet.snapid < connection.lastRecievedSnapshot) return;
player.getInterpolator().read(player.x, player.y, packet.x, packet.y, packet.timeSent, packet.rotation, packet.baseRotation); boolean verifyPosition = !player.isDead() && !debug && !headless;
if(connection.lastRecievedTime == 0) connection.lastRecievedTime = TimeUtils.millis() - 16;
long elapsed = TimeUtils.timeSinceMillis(connection.lastRecievedTime);
//extra 1.1x multiplicaton is added just in case
float maxMove = elapsed / 1000f * 60f * player.mech.maxSpeed * 1.1f;
player.pointerX = packet.pointerX;
player.pointerY = packet.pointerY;
vector.set(packet.x - player.getInterpolator().target.x, packet.y - player.getInterpolator().target.y);
vector.limit(maxMove);
float prevx = player.x, prevy = player.y;
player.set(player.getInterpolator().target.x, player.getInterpolator().target.y);
player.move(vector.x, vector.y);
float newx = player.x, newy = player.y;
if(!verifyPosition){
player.x = prevx;
player.y = prevy;
newx = packet.x;
newy = packet.y;
}else if(Vector2.dst(packet.x, packet.y, newx, newy) > correctDist){
Call.onPositionSet(id, newx, newy); //teleport and correct position when necessary
}
//reset player to previous synced position so it gets interpolated
player.x = prevx;
player.y = prevy;
//set interpolator target to *new* position so it moves toward it
player.getInterpolator().read(player.x, player.y, newx, newy, packet.timeSent, packet.rotation, packet.baseRotation);
player.getVelocity().set(packet.xv, packet.yv); //only for visual calculation purposes, doesn't actually update the player player.getVelocity().set(packet.xv, packet.yv); //only for visual calculation purposes, doesn't actually update the player
connection.lastSnapshotID = packet.lastSnapshot; connection.lastSnapshotID = packet.lastSnapshot;
connection.lastRecievedSnapshot = packet.snapid; connection.lastRecievedSnapshot = packet.snapid;
connection.lastRecievedTime = TimeUtils.millis();
}); });
Net.handleServer(InvokePacket.class, (id, packet) -> RemoteReadServer.readPacket(packet.writeBuffer, packet.type, connections.get(id))); Net.handleServer(InvokePacket.class, (id, packet) -> RemoteReadServer.readPacket(packet.writeBuffer, packet.type, connections.get(id)));

View File

@@ -156,9 +156,17 @@ public class World extends Module{
generating = true; generating = true;
} }
/**Call to signify the end of map loading. /**Call to signify the end of map loading. Updates tile occlusions and sets up physics for the world.
* A WorldLoadEvent will be fire.*/ * A WorldLoadEvent will be fire.*/
public void endMapLoad(){ public void endMapLoad(){
for(int x = 0; x < tiles.length; x ++) {
for (int y = 0; y < tiles[0].length; y++) {
tiles[x][y].updateOcclusion();
}
}
EntityPhysics.resizeTree(0, 0, tiles.length * tilesize, tiles[0].length * tilesize);
generating = false; generating = false;
Events.fire(WorldLoadEvent.class); Events.fire(WorldLoadEvent.class);
} }

View File

@@ -18,6 +18,7 @@ import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.graphics.Trail; import io.anuke.mindustry.graphics.Trail;
import io.anuke.mindustry.net.In; import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
@@ -28,10 +29,7 @@ import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.entities.trait.SolidTrait; import io.anuke.ucore.entities.trait.SolidTrait;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.*;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.ThreadQueue;
import io.anuke.ucore.util.Timer;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
@@ -40,10 +38,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Player extends Unit implements BuilderTrait, CarryTrait { public class Player extends Unit implements BuilderTrait, CarryTrait {
private static final float walkSpeed = 1.1f; private static final float debugSpeed = 1.8f;
private static final float flySpeed = 0.4f;
private static final float flyMaxSpeed = 3f;
private static final float dashSpeed = 1.8f;
private static final Vector2 movement = new Vector2(); private static final Vector2 movement = new Vector2();
public static int typeID = -1; public static int typeID = -1;
@@ -56,9 +51,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
public float baseRotation; public float baseRotation;
public float pointerX, pointerY;
public String name = "name"; public String name = "name";
public String uuid; public String uuid;
public boolean isAdmin, isTransferring; public boolean isAdmin, isTransferring, isShooting;
public Color color = new Color(); public Color color = new Color();
public Array<Upgrade> upgrades = new Array<>(); public Array<Upgrade> upgrades = new Array<>();
@@ -224,7 +220,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
@Override @Override
public void removed() { public void removed() {
dropCarry(); dropCarryLocal();
} }
@Override @Override
@@ -388,8 +384,8 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
if(isDead()){ if(isDead()){
CoreEntity entity = (CoreEntity)getClosestCore(); CoreEntity entity = (CoreEntity)getClosestCore();
if(!respawning && entity != null && entity.trySetPlayer(this)){ if (!respawning && entity != null) {
respawning = true; entity.trySetPlayer(this);
} }
return; return;
} }
@@ -398,6 +394,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
interpolate(); interpolate();
updateBuilding(this); //building happens even with non-locals updateBuilding(this); //building happens even with non-locals
status.update(this); //status effect updating also happens with non locals for effect purposes status.update(this); //status effect updating also happens with non locals for effect purposes
if(Net.server()){
updateShooting(); //server simulates player shooting
}
return; return;
} }
@@ -424,13 +424,13 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
Tile tile = world.tileWorld(x, y); Tile tile = world.tileWorld(x, y);
//if player is in solid block //if player is in solid block
if(tile != null && tile.solid()) { if(tile != null && tile.solid() && !noclip) {
damage(health + 1); //die instantly damage(health + 1); //die instantly
} }
if(ui.chatfrag.chatOpen()) return; if(ui.chatfrag.chatOpen()) return;
float speed = Inputs.keyDown("dash") ? (debug ? Player.dashSpeed * 5f : Player.dashSpeed) : Player.walkSpeed; float speed = Inputs.keyDown("dash") && debug ? 5f : mech.speed;
float carrySlowdown = 0.3f; float carrySlowdown = 0.3f;
speed *= ((1f-carrySlowdown) + (inventory.hasItem() ? (float)inventory.getItem().amount/inventory.capacity(): 1f) * carrySlowdown); speed *= ((1f-carrySlowdown) + (inventory.hasItem() ? (float)inventory.getItem().amount/inventory.capacity(): 1f) * carrySlowdown);
@@ -452,16 +452,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
movement.y += ya*speed; movement.y += ya*speed;
movement.x += xa*speed; movement.x += xa*speed;
boolean shooting = isShooting(); Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(),
Vars.control.input(playerIndex).getMouseY());
if(shooting){ pointerX = vec.x;
Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(), pointerY = vec.y;
Vars.control.input(playerIndex).getMouseY()); updateShooting();
float vx = vec.x, vy = vec.y;
weapon.update(this, true, vx, vy);
weapon.update(this, false, vx, vy);
}
movement.limit(speed); movement.limit(speed);
@@ -474,7 +469,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f); baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f);
} }
if(!shooting){ if(!isShooting()){
if(!movement.isZero()) { if(!movement.isZero()) {
rotation = Mathf.slerpDelta(rotation, movement.angle(), 0.13f); rotation = Mathf.slerpDelta(rotation, movement.angle(), 0.13f);
} }
@@ -484,6 +479,13 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
} }
} }
protected void updateShooting(){
if(isShooting()){
weapon.update(this, true, pointerX, pointerY);
weapon.update(this, false, pointerX, pointerY);
}
}
protected void updateFlying(){ protected void updateFlying(){
if(Units.invalidateTarget(target, this)){ if(Units.invalidateTarget(target, this)){
target = null; target = null;
@@ -506,7 +508,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
pickupTarget = null; pickupTarget = null;
} }
movement.set(targetX - x, targetY - y).limit(flySpeed); movement.set(targetX - x, targetY - y).limit(mech.speed);
movement.setAngle(Mathf.slerpDelta(movement.angle(), velocity.angle(), 0.05f)); movement.setAngle(Mathf.slerpDelta(movement.angle(), velocity.angle(), 0.05f));
if(distanceTo(targetX, targetY) < attractDst){ if(distanceTo(targetX, targetY) < attractDst){
@@ -514,7 +516,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
} }
velocity.add(movement); velocity.add(movement);
updateVelocityStatus(0.1f, flyMaxSpeed); updateVelocityStatus(0.1f, mech.maxSpeed);
//hovering effect //hovering effect
x += Mathf.sin(Timers.time() + id * 999, 25f, 0.08f); x += Mathf.sin(Timers.time() + id * 999, 25f, 0.08f);
@@ -531,7 +533,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
//autofire: mobile only! //autofire: mobile only!
if(mobile) { if(mobile) {
boolean lastShooting = isShooting;
if (target == null) { if (target == null) {
isShooting = false;
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange()); target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
} else { } else {
//rotate toward and shoot the target //rotate toward and shoot the target
@@ -540,16 +545,24 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
Vector2 intercept = Vector2 intercept =
Predict.intercept(x, y, target.getX(), target.getY(), target.getVelocity().x - velocity.x, target.getVelocity().y - velocity.y, inventory.getAmmo().bullet.speed); Predict.intercept(x, y, target.getX(), target.getY(), target.getVelocity().x - velocity.x, target.getVelocity().y - velocity.y, inventory.getAmmo().bullet.speed);
weapon.update(this, true, intercept.x, intercept.y); pointerX = intercept.x;
weapon.update(this, false, intercept.x, intercept.y); pointerY = intercept.y;
updateShooting();
isShooting = true;
}
//update status of shooting to server
if(lastShooting != isShooting){
CallEntity.setShooting(isShooting);
} }
}else if(isShooting()){ //desktop shooting, TODO }else if(isShooting()){ //desktop shooting, TODO
Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(), Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(),
Vars.control.input(playerIndex).getMouseY()); Vars.control.input(playerIndex).getMouseY());
float vx = vec.x, vy = vec.y; pointerX = vec.x;
pointerY = vec.y;
weapon.update(this, true, vx, vy); updateShooting();
weapon.update(this, false, vx, vy);
} }
} }
} }
@@ -578,7 +591,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
} }
public boolean isShooting(){ public boolean isShooting(){
return control.input(playerIndex).canShoot() && control.input(playerIndex).isShooting() && inventory.hasAmmo(); return isShooting && inventory.hasAmmo();
}
public void setRespawning(){
respawning = true;
} }
@Override @Override
@@ -601,7 +618,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
if(isLocal){ if(isLocal){
stream.writeInt(playerIndex); stream.writeInt(playerIndex);
super.writeSave(stream); super.writeSave(stream, false);
stream.writeByte(upgrades.size); stream.writeByte(upgrades.size);
for(Upgrade u : upgrades){ for(Upgrade u : upgrades){
@@ -632,7 +649,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
@Override @Override
public void write(DataOutput buffer) throws IOException { public void write(DataOutput buffer) throws IOException {
super.writeSave(buffer); super.writeSave(buffer, !isLocal);
buffer.writeUTF(name); buffer.writeUTF(name);
buffer.writeInt(Color.rgba8888(color)); buffer.writeInt(Color.rgba8888(color));
buffer.writeBoolean(dead); buffer.writeBoolean(dead);

View File

@@ -105,16 +105,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
@Override @Override
public void writeSave(DataOutput stream) throws IOException { public void writeSave(DataOutput stream) throws IOException {
stream.writeByte(team.ordinal()); writeSave(stream, false);
stream.writeFloat(x);
stream.writeFloat(y);
stream.writeByte((byte)(Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeByte((byte)(Mathf.clamp(velocity.y, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeShort((short)(rotation*2));
stream.writeShort((short)health);
stream.writeByte(status.current().id);
stream.writeShort((short)(status.getTime()*2));
inventory.write(stream);
} }
@Override @Override
@@ -139,6 +130,19 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
this.status.set(StatusEffect.getByID(effect), etime); this.status.set(StatusEffect.getByID(effect), etime);
} }
public void writeSave(DataOutput stream, boolean net) throws IOException {
stream.writeByte(team.ordinal());
stream.writeFloat(net ? interpolator.target.x : x);
stream.writeFloat(net ? interpolator.target.y : y);
stream.writeByte((byte)(Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeByte((byte)(Mathf.clamp(velocity.y, -maxAbsVelocity, maxAbsVelocity) * velocityPercision));
stream.writeShort((short)(rotation*2));
stream.writeShort((short)health);
stream.writeByte(status.current().id);
stream.writeShort((short)(status.getTime()*2));
inventory.write(stream);
}
public StatusEffect getStatus(){ public StatusEffect getStatus(){
return status.current(); return status.current();
} }

View File

@@ -1,6 +1,11 @@
package io.anuke.mindustry.entities.traits; package io.anuke.mindustry.entities.traits;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.content.fx.UnitFx; import io.anuke.mindustry.content.fx.UnitFx;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.net.In;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.entities.trait.SolidTrait; import io.anuke.ucore.entities.trait.SolidTrait;
@@ -17,23 +22,36 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
carry(null); carry(null);
} }
default void dropCarryLocal(){
setCarryOf(null, this, null);
}
/**Do not override unless absolutely necessary. /**Do not override unless absolutely necessary.
* Carries a unit. To drop a unit, call with {@code null}.*/ * Carries a unit. To drop a unit, call with {@code null}.*/
default void carry(CarriableTrait unit){ default void carry(CarriableTrait unit){
if(getCarry() != null){ //already carrying something, drop it CallEntity.setCarryOf(this instanceof Player ? (Player)this : null, this, unit);
}
@Remote(called = Loc.server, targets = Loc.both, forward = true, in = In.entities)
static void setCarryOf(Player player, CarryTrait trait, CarriableTrait unit){
if(player != null){ //when a server recieves this called from a player, set the carrier to the player.
trait = player;
}
if(trait.getCarry() != null){ //already carrying something, drop it
//drop current //drop current
Effects.effect(UnitFx.unitDrop, getCarry()); Effects.effect(UnitFx.unitDrop, trait.getCarry());
getCarry().setCarrier(null); trait.getCarry().setCarrier(null);
setCarry(null); trait.setCarry(null);
if(unit != null){ if(unit != null){
carry(unit); //now carry this new thing trait.carry(unit); //now carry this new thing
} }
}else if(unit != null){ //not currently carrying anything, make sure it's not null }else if(unit != null){ //not currently carrying anything, make sure it's not null
setCarry(unit); trait.setCarry(unit);
unit.setCarrier(this); unit.setCarrier(trait);
Effects.effect(UnitFx.unitPickup, this); Effects.effect(UnitFx.unitPickup, trait);
} }
} }
} }

View File

@@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion;
import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.Blocks;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult; import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult;
import io.anuke.mindustry.input.PlaceUtils.NormalizeResult; import io.anuke.mindustry.input.PlaceUtils.NormalizeResult;
@@ -130,6 +131,10 @@ public class DesktopInput extends InputHandler{
mode = none; mode = none;
} }
if(player.isShooting && !canShoot()){
CallEntity.setShooting(false);
}
if(isPlacing()){ if(isPlacing()){
cursorType = hand; cursorType = hand;
selectScale = Mathf.lerpDelta(selectScale, 1f, 0.2f); selectScale = Mathf.lerpDelta(selectScale, 1f, 0.2f);
@@ -188,7 +193,7 @@ public class DesktopInput extends InputHandler{
//only begin shooting if there's no cursor event //only begin shooting if there's no cursor event
if(!tileTapped(cursor) && player.getPlaceQueue().size == 0 && !tryTapPlayer(worldx, worldy) && !droppingItem && if(!tileTapped(cursor) && player.getPlaceQueue().size == 0 && !tryTapPlayer(worldx, worldy) && !droppingItem &&
!tryBeginMine(cursor) && player.getMineTile() == null){ !tryBeginMine(cursor) && player.getMineTile() == null){
shooting = true; CallEntity.setShooting(true);
} }
} }
}else if(button == Buttons.RIGHT){ //right = begin breaking }else if(button == Buttons.RIGHT){ //right = begin breaking
@@ -206,7 +211,7 @@ public class DesktopInput extends InputHandler{
@Override @Override
public boolean touchUp (int screenX, int screenY, int pointer, int button) { public boolean touchUp (int screenX, int screenY, int pointer, int button) {
if(button == Buttons.LEFT){ if(button == Buttons.LEFT){
shooting = false; CallEntity.setShooting(false);
} }
if(player.isDead() || state.is(State.menu) || ui.hasDialog()) return false; if(player.isDead() || state.is(State.menu) || ui.hasDialog()) return false;

View File

@@ -46,7 +46,6 @@ public abstract class InputHandler extends InputAdapter{
public Recipe recipe; public Recipe recipe;
public int rotation; public int rotation;
public boolean droppingItem; public boolean droppingItem;
public boolean shooting;
public InputHandler(Player player){ public InputHandler(Player player){
this.player = player; this.player = player;
@@ -189,10 +188,6 @@ public abstract class InputHandler extends InputAdapter{
public boolean canShoot(){ public boolean canShoot(){
return recipe == null && !ui.hasMouse() && !onConfigurable() && !isDroppingItem(); return recipe == null && !ui.hasMouse() && !onConfigurable() && !isDroppingItem();
} }
public boolean isShooting(){
return shooting;
}
public boolean onConfigurable(){ public boolean onConfigurable(){
return false; return false;
@@ -262,6 +257,11 @@ public abstract class InputHandler extends InputAdapter{
player.addBuildRequest(new BuildRequest(tile.x, tile.y)); player.addBuildRequest(new BuildRequest(tile.x, tile.y));
} }
@Remote(targets = Loc.client, called = Loc.both, in = In.entities)
public static void setShooting(Player player, boolean on){
player.isShooting = on;
}
@Remote(targets = Loc.both, called = Loc.server, in = In.entities) @Remote(targets = Loc.both, called = Loc.server, in = In.entities)
public static void dropItem(Player player, float angle){ public static void dropItem(Player player, float angle){
if(Net.server() && !player.inventory.hasItem()){ if(Net.server() && !player.inventory.hasItem()){

View File

@@ -6,6 +6,8 @@ import io.anuke.annotations.Annotations.WriteClass;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.entities.traits.CarriableTrait;
import io.anuke.mindustry.entities.traits.CarryTrait;
import io.anuke.mindustry.entities.units.BaseUnit; import io.anuke.mindustry.entities.units.BaseUnit;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.net.Packets.KickReason; import io.anuke.mindustry.net.Packets.KickReason;
@@ -51,6 +53,39 @@ public class TypeIO {
return (Unit)Entities.getGroup(gid).getByID(id); return (Unit)Entities.getGroup(gid).getByID(id);
} }
@WriteClass(CarriableTrait.class)
public static void writeCarriable(ByteBuffer buffer, CarriableTrait unit){
if(unit == null){
buffer.put((byte)-1);
return;
}
buffer.put((byte)unit.getGroup().getID());
buffer.putInt(unit.getID());
}
@ReadClass(CarriableTrait.class)
public static CarriableTrait readCarriable(ByteBuffer buffer){
byte gid = buffer.get();
if(gid == -1){
return null;
}
int id = buffer.getInt();
return (CarriableTrait)Entities.getGroup(gid).getByID(id);
}
@WriteClass(CarryTrait.class)
public static void writeCarry(ByteBuffer buffer, CarryTrait unit){
buffer.put((byte)unit.getGroup().getID());
buffer.putInt(unit.getID());
}
@ReadClass(CarryTrait.class)
public static CarryTrait readCarry(ByteBuffer buffer){
byte gid = buffer.get();
int id = buffer.getInt();
return (CarryTrait)Entities.getGroup(gid).getByID(id);
}
@WriteClass(BaseUnit.class) @WriteClass(BaseUnit.class)
public static void writeBaseUnit(ByteBuffer buffer, BaseUnit unit){ public static void writeBaseUnit(ByteBuffer buffer, BaseUnit unit){
buffer.put((byte)unit.getGroup().getID()); buffer.put((byte)unit.getGroup().getID());

View File

@@ -92,9 +92,11 @@ public class Save16 extends SaveFileVersion {
for(int y = 0; y < height; y ++) { for(int y = 0; y < height; y ++) {
byte floorid = stream.readByte(); byte floorid = stream.readByte();
byte wallid = stream.readByte(); byte wallid = stream.readByte();
byte elevation = stream.readByte();
Tile tile = new Tile(x, y, floorid, wallid); Tile tile = new Tile(x, y, floorid, wallid);
tile.elevation = elevation;
if (wallid == Blocks.blockpart.id) { if (wallid == Blocks.blockpart.id) {
tile.link = stream.readByte(); tile.link = stream.readByte();
@@ -190,8 +192,9 @@ public class Save16 extends SaveFileVersion {
for(int y = 0; y < world.height(); y ++){ for(int y = 0; y < world.height(); y ++){
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
stream.writeByte(tile.floor().id); //floor ID stream.writeByte(tile.floor().id);
stream.writeByte(tile.block().id); //wall ID stream.writeByte(tile.block().id);
stream.writeByte(tile.elevation);
if(tile.block() instanceof BlockPart){ if(tile.block() instanceof BlockPart){
stream.writeByte(tile.link); stream.writeByte(tile.link);

View File

@@ -18,6 +18,8 @@ public abstract class NetConnection {
/**ID of last recieved client snapshot.*/ /**ID of last recieved client snapshot.*/
public int lastRecievedSnapshot = -1; public int lastRecievedSnapshot = -1;
/**Timestamp of last recieved snapshot.*/
public long lastRecievedTime;
public NetConnection(int id, String address){ public NetConnection(int id, String address){
this.id = id; this.id = id;

View File

@@ -63,9 +63,9 @@ public class NetworkIO {
for(int y = 0; y < world.height(); y ++){ for(int y = 0; y < world.height(); y ++){
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
//TODO will break if block number gets over BYTE_MAX
stream.writeByte(tile.floor().id); //floor ID stream.writeByte(tile.floor().id); //floor ID
stream.writeByte(tile.block().id); //block ID stream.writeByte(tile.block().id); //block ID
stream.writeByte(tile.elevation);
if(tile.block() instanceof BlockPart){ if(tile.block() instanceof BlockPart){
stream.writeByte(tile.link); stream.writeByte(tile.link);
@@ -162,9 +162,12 @@ public class NetworkIO {
for(int y = 0; y < height; y ++){ for(int y = 0; y < height; y ++){
byte floorid = stream.readByte(); byte floorid = stream.readByte();
byte blockid = stream.readByte(); byte blockid = stream.readByte();
byte elevation = stream.readByte();
Tile tile = new Tile(x, y, floorid, blockid); Tile tile = new Tile(x, y, floorid, blockid);
tile.elevation = elevation;
if(tile.block() == Blocks.blockpart){ if(tile.block() == Blocks.blockpart){
tile.link = stream.readByte(); tile.link = stream.readByte();
} }
@@ -189,8 +192,6 @@ public class NetworkIO {
} }
} }
EntityPhysics.resizeTree(0, 0, width * tilesize, height * tilesize);
player.reset(); player.reset();
state.teams = new TeamInfo(); state.teams = new TeamInfo();

View File

@@ -101,7 +101,7 @@ public class Packets {
public int snapid; public int snapid;
public long timeSent; public long timeSent;
//player snapshot data //player snapshot data
public float x, y, rotation, baseRotation, xv, yv; public float x, y, pointerX, pointerY, rotation, baseRotation, xv, yv;
@Override @Override
public void write(ByteBuffer buffer) { public void write(ByteBuffer buffer) {
@@ -113,6 +113,8 @@ public class Packets {
buffer.putFloat(player.x); buffer.putFloat(player.x);
buffer.putFloat(player.y); buffer.putFloat(player.y);
buffer.putFloat(player.pointerX);
buffer.putFloat(player.pointerY);
buffer.put((byte)(Mathf.clamp(player.getVelocity().x, -Unit.maxAbsVelocity, Unit.maxAbsVelocity) * Unit.velocityPercision)); buffer.put((byte)(Mathf.clamp(player.getVelocity().x, -Unit.maxAbsVelocity, Unit.maxAbsVelocity) * Unit.velocityPercision));
buffer.put((byte)(Mathf.clamp(player.getVelocity().y, -Unit.maxAbsVelocity, Unit.maxAbsVelocity) * Unit.velocityPercision)); buffer.put((byte)(Mathf.clamp(player.getVelocity().y, -Unit.maxAbsVelocity, Unit.maxAbsVelocity) * Unit.velocityPercision));
@@ -129,6 +131,8 @@ public class Packets {
x = buffer.getFloat(); x = buffer.getFloat();
y = buffer.getFloat(); y = buffer.getFloat();
pointerX = buffer.getFloat();
pointerY = buffer.getFloat();
xv = buffer.get() / Unit.velocityPercision; xv = buffer.get() / Unit.velocityPercision;
yv = buffer.get() / Unit.velocityPercision; yv = buffer.get() / Unit.velocityPercision;
rotation = buffer.getShort()/2f; rotation = buffer.getShort()/2f;

View File

@@ -5,6 +5,8 @@ import io.anuke.ucore.graphics.Draw;
public class Mech extends Upgrade { public class Mech extends Upgrade {
public boolean flying; public boolean flying;
public float speed = 1.1f;
public float maxSpeed = 1.1f;
public float mass = 1f; public float mass = 1f;
public int drillPower = -1; public int drillPower = -1;
public float carryWeight = 1f; public float carryWeight = 1f;

View File

@@ -2,14 +2,16 @@ package io.anuke.mindustry.type;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectMap;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.annotations.Annotations.Loc; import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.fx.Fx; import io.anuke.mindustry.content.fx.Fx;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.gen.CallEntity; import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.net.In; import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
@@ -82,7 +84,13 @@ public class Weapon extends Upgrade {
} }
public void shoot(Player p, float x, float y, float angle, boolean left){ public void shoot(Player p, float x, float y, float angle, boolean left){
CallEntity.onShootWeapon(p, this, x, y, angle, left); if(Net.client()){
//call it directly, don't invoke on server
shootDirect(p, this, x, y, angle, left);
}else{
CallEntity.onShootWeapon(p, this, x, y, angle, left);
}
p.inventory.useAmmo(); p.inventory.useAmmo();
} }
@@ -101,8 +109,18 @@ public class Weapon extends Upgrade {
Bullet.create(owner.inventory.getAmmo().bullet, owner, x + tr.x, y + tr.y, angle); Bullet.create(owner.inventory.getAmmo().bullet, owner, x + tr.x, y + tr.y, angle);
} }
@Remote(targets = Loc.both, called = Loc.both, in = In.entities, unreliable = true, forward = true) @Remote(targets = Loc.server, called = Loc.both, in = In.entities, unreliable = true)
public static void onShootWeapon(Player player, Weapon weapon, float x, float y, float rotation, boolean left){ public static void onShootWeapon(Player player, Weapon weapon, float x, float y, float rotation, boolean left){
//clients do not see their own shoot events: they are simulated completely clientside to prevent laggy visuals
//messing with the firerate or any other stats does not affect the server (take that, script kiddies!)
if(Net.client() && player == Vars.players[0]){
return;
}
shootDirect(player, weapon, x, y, rotation, left);
}
public static void shootDirect(Player player, Weapon weapon, float x, float y, float rotation, boolean left){
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> weapon.bullet(player, x, y, f + Mathf.range(weapon.inaccuracy))); Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> weapon.bullet(player, x, y, f + Mathf.range(weapon.inaccuracy)));
AmmoType type = player.inventory.getAmmo(); AmmoType type = player.inventory.getAmmo();

View File

@@ -155,15 +155,7 @@ public class CoreBlock extends StorageBlock {
} }
if(entity.progress >= 1f){ if(entity.progress >= 1f){
Effects.effect(Fx.spawn, entity); CallBlocks.onPlayerRespawn(tile, entity.currentPlayer);
CallBlocks.setCoreSolid(tile, false);
entity.progress = 0;
entity.currentPlayer.heal();
entity.currentPlayer.rotation = 90f;
entity.currentPlayer.baseRotation = 90f;
entity.currentPlayer.set(tile.drawx(), tile.drawy());
entity.currentPlayer.add();
entity.currentPlayer = null;
} }
}else{ }else{
entity.heat = Mathf.lerpDelta(entity.heat, 0f, 0.1f); entity.heat = Mathf.lerpDelta(entity.heat, 0f, 0.1f);
@@ -193,6 +185,30 @@ public class CoreBlock extends StorageBlock {
return new CoreEntity(); return new CoreEntity();
} }
@Remote(called = Loc.server, in = In.blocks)
public static void onPlayerRespawn(Tile tile, Player player){
CoreEntity entity = tile.entity();
Effects.effect(Fx.spawn, entity);
entity.solid = false;
entity.progress = 0;
entity.currentPlayer = player;
entity.currentPlayer.heal();
entity.currentPlayer.rotation = 90f;
entity.currentPlayer.baseRotation = 90f;
entity.currentPlayer.setNet(tile.drawx(), tile.drawy());
entity.currentPlayer.add();
entity.currentPlayer = null;
}
@Remote(called = Loc.server, in = In.blocks)
public static void onCorePlayerSet(Tile tile, Player player){
CoreEntity entity = tile.entity();
entity.currentPlayer = player;
entity.progress = 0f;
player.set(tile.drawx(), tile.drawy());
player.setRespawning();
}
@Remote(called = Loc.server, in = In.blocks) @Remote(called = Loc.server, in = In.blocks)
public static void setCoreSolid(Tile tile, boolean solid){ public static void setCoreSolid(Tile tile, boolean solid){
CoreEntity entity = tile.entity(); CoreEntity entity = tile.entity();
@@ -206,12 +222,10 @@ public class CoreBlock extends StorageBlock {
float time; float time;
float heat; float heat;
public boolean trySetPlayer(Player player){ public void trySetPlayer(Player player){
if(currentPlayer != null) return false; if(currentPlayer == null){
player.set(tile.drawx(), tile.drawy()); CallBlocks.onCorePlayerSet(tile, player);
currentPlayer = player; }
progress = 0f;
return true;
} }
@Override @Override