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){{
drillPower = 1;
speed = 1.1f;
maxSpeed = 1.1f;
}};
standardShip = new Mech("standard-ship", true){{
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 Timer timer = new Timer(5);
/**Whether the client is currently conencting.*/
/**Whether the client is currently connecting.*/
private boolean connecting = false;
/**If true, no message will be shown on disconnect.*/
private boolean quiet = false;
@@ -178,6 +178,12 @@ public class NetClient extends Module {
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)
public static void onSnapshot(byte[] snapshot, int snapshotID){
//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.Colors;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Base64Coder;
import com.badlogic.gdx.utils.IntMap;
@@ -40,6 +41,9 @@ import static io.anuke.mindustry.Vars.*;
public class NetServer extends Module{
private final static float serverSyncTime = 4, kickDuration = 30 * 1000;
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();
@@ -153,10 +157,46 @@ public class NetServer extends Module{
NetConnection connection = Net.getConnection(id);
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
connection.lastSnapshotID = packet.lastSnapshot;
connection.lastRecievedSnapshot = packet.snapid;
connection.lastRecievedTime = TimeUtils.millis();
});
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;
}
/**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.*/
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;
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.Trail;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Block;
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.graphics.Draw;
import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.ThreadQueue;
import io.anuke.ucore.util.Timer;
import io.anuke.ucore.util.*;
import java.io.DataInput;
import java.io.DataOutput;
@@ -40,10 +38,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*;
public class Player extends Unit implements BuilderTrait, CarryTrait {
private static final float walkSpeed = 1.1f;
private static final float flySpeed = 0.4f;
private static final float flyMaxSpeed = 3f;
private static final float dashSpeed = 1.8f;
private static final float debugSpeed = 1.8f;
private static final Vector2 movement = new Vector2();
public static int typeID = -1;
@@ -56,9 +51,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
public float baseRotation;
public float pointerX, pointerY;
public String name = "name";
public String uuid;
public boolean isAdmin, isTransferring;
public boolean isAdmin, isTransferring, isShooting;
public Color color = new Color();
public Array<Upgrade> upgrades = new Array<>();
@@ -224,7 +220,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
@Override
public void removed() {
dropCarry();
dropCarryLocal();
}
@Override
@@ -388,8 +384,8 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
if(isDead()){
CoreEntity entity = (CoreEntity)getClosestCore();
if(!respawning && entity != null && entity.trySetPlayer(this)){
respawning = true;
if (!respawning && entity != null) {
entity.trySetPlayer(this);
}
return;
}
@@ -398,6 +394,10 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
interpolate();
updateBuilding(this); //building happens even with non-locals
status.update(this); //status effect updating also happens with non locals for effect purposes
if(Net.server()){
updateShooting(); //server simulates player shooting
}
return;
}
@@ -424,13 +424,13 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
Tile tile = world.tileWorld(x, y);
//if player is in solid block
if(tile != null && tile.solid()) {
if(tile != null && tile.solid() && !noclip) {
damage(health + 1); //die instantly
}
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;
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.x += xa*speed;
boolean shooting = isShooting();
if(shooting){
Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(),
Vars.control.input(playerIndex).getMouseY());
float vx = vec.x, vy = vec.y;
weapon.update(this, true, vx, vy);
weapon.update(this, false, vx, vy);
}
Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(),
Vars.control.input(playerIndex).getMouseY());
pointerX = vec.x;
pointerY = vec.y;
updateShooting();
movement.limit(speed);
@@ -474,7 +469,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f);
}
if(!shooting){
if(!isShooting()){
if(!movement.isZero()) {
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(){
if(Units.invalidateTarget(target, this)){
target = null;
@@ -506,7 +508,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
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));
if(distanceTo(targetX, targetY) < attractDst){
@@ -514,7 +516,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
}
velocity.add(movement);
updateVelocityStatus(0.1f, flyMaxSpeed);
updateVelocityStatus(0.1f, mech.maxSpeed);
//hovering effect
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!
if(mobile) {
boolean lastShooting = isShooting;
if (target == null) {
isShooting = false;
target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange());
} else {
//rotate toward and shoot the target
@@ -540,16 +545,24 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
Vector2 intercept =
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);
weapon.update(this, false, intercept.x, intercept.y);
pointerX = intercept.x;
pointerY = intercept.y;
updateShooting();
isShooting = true;
}
//update status of shooting to server
if(lastShooting != isShooting){
CallEntity.setShooting(isShooting);
}
}else if(isShooting()){ //desktop shooting, TODO
Vector2 vec = Graphics.world(Vars.control.input(playerIndex).getMouseX(),
Vars.control.input(playerIndex).getMouseY());
float vx = vec.x, vy = vec.y;
pointerX = vec.x;
pointerY = vec.y;
weapon.update(this, true, vx, vy);
weapon.update(this, false, vx, vy);
updateShooting();
}
}
}
@@ -578,7 +591,11 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
}
public boolean isShooting(){
return control.input(playerIndex).canShoot() && control.input(playerIndex).isShooting() && inventory.hasAmmo();
return isShooting && inventory.hasAmmo();
}
public void setRespawning(){
respawning = true;
}
@Override
@@ -601,7 +618,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
if(isLocal){
stream.writeInt(playerIndex);
super.writeSave(stream);
super.writeSave(stream, false);
stream.writeByte(upgrades.size);
for(Upgrade u : upgrades){
@@ -632,7 +649,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait {
@Override
public void write(DataOutput buffer) throws IOException {
super.writeSave(buffer);
super.writeSave(buffer, !isLocal);
buffer.writeUTF(name);
buffer.writeInt(Color.rgba8888(color));
buffer.writeBoolean(dead);

View File

@@ -105,16 +105,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
@Override
public void writeSave(DataOutput stream) throws IOException {
stream.writeByte(team.ordinal());
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);
writeSave(stream, false);
}
@Override
@@ -139,6 +130,19 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
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(){
return status.current();
}

View File

@@ -1,6 +1,11 @@
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.entities.Player;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.net.In;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.entities.trait.SolidTrait;
@@ -17,23 +22,36 @@ public interface CarryTrait extends TeamTrait, SolidTrait, TargetTrait{
carry(null);
}
default void dropCarryLocal(){
setCarryOf(null, this, null);
}
/**Do not override unless absolutely necessary.
* Carries a unit. To drop a unit, call with {@code null}.*/
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
Effects.effect(UnitFx.unitDrop, getCarry());
getCarry().setCarrier(null);
setCarry(null);
Effects.effect(UnitFx.unitDrop, trait.getCarry());
trait.getCarry().setCarrier(null);
trait.setCarry(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
setCarry(unit);
unit.setCarrier(this);
trait.setCarry(unit);
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.core.GameState.State;
import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.graphics.Palette;
import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult;
import io.anuke.mindustry.input.PlaceUtils.NormalizeResult;
@@ -130,6 +131,10 @@ public class DesktopInput extends InputHandler{
mode = none;
}
if(player.isShooting && !canShoot()){
CallEntity.setShooting(false);
}
if(isPlacing()){
cursorType = hand;
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
if(!tileTapped(cursor) && player.getPlaceQueue().size == 0 && !tryTapPlayer(worldx, worldy) && !droppingItem &&
!tryBeginMine(cursor) && player.getMineTile() == null){
shooting = true;
CallEntity.setShooting(true);
}
}
}else if(button == Buttons.RIGHT){ //right = begin breaking
@@ -206,7 +211,7 @@ public class DesktopInput extends InputHandler{
@Override
public boolean touchUp (int screenX, int screenY, int pointer, int button) {
if(button == Buttons.LEFT){
shooting = false;
CallEntity.setShooting(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 int rotation;
public boolean droppingItem;
public boolean shooting;
public InputHandler(Player player){
this.player = player;
@@ -189,10 +188,6 @@ public abstract class InputHandler extends InputAdapter{
public boolean canShoot(){
return recipe == null && !ui.hasMouse() && !onConfigurable() && !isDroppingItem();
}
public boolean isShooting(){
return shooting;
}
public boolean onConfigurable(){
return false;
@@ -262,6 +257,11 @@ public abstract class InputHandler extends InputAdapter{
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)
public static void dropItem(Player player, float angle){
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.Unit;
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.game.Team;
import io.anuke.mindustry.net.Packets.KickReason;
@@ -51,6 +53,39 @@ public class TypeIO {
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)
public static void writeBaseUnit(ByteBuffer buffer, BaseUnit unit){
buffer.put((byte)unit.getGroup().getID());

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,8 @@ import io.anuke.ucore.graphics.Draw;
public class Mech extends Upgrade {
public boolean flying;
public float speed = 1.1f;
public float maxSpeed = 1.1f;
public float mass = 1f;
public int drillPower = -1;
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.utils.ObjectMap;
import io.anuke.annotations.Annotations.Remote;
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.entities.Player;
import io.anuke.mindustry.entities.Unit;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.gen.CallEntity;
import io.anuke.mindustry.net.In;
import io.anuke.mindustry.net.Net;
import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Effects.Effect;
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){
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();
}
@@ -101,8 +109,18 @@ public class Weapon extends Upgrade {
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){
//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)));
AmmoType type = player.inventory.getAmmo();

View File

@@ -155,15 +155,7 @@ public class CoreBlock extends StorageBlock {
}
if(entity.progress >= 1f){
Effects.effect(Fx.spawn, entity);
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;
CallBlocks.onPlayerRespawn(tile, entity.currentPlayer);
}
}else{
entity.heat = Mathf.lerpDelta(entity.heat, 0f, 0.1f);
@@ -193,6 +185,30 @@ public class CoreBlock extends StorageBlock {
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)
public static void setCoreSolid(Tile tile, boolean solid){
CoreEntity entity = tile.entity();
@@ -206,12 +222,10 @@ public class CoreBlock extends StorageBlock {
float time;
float heat;
public boolean trySetPlayer(Player player){
if(currentPlayer != null) return false;
player.set(tile.drawx(), tile.drawy());
currentPlayer = player;
progress = 0f;
return true;
public void trySetPlayer(Player player){
if(currentPlayer == null){
CallBlocks.onCorePlayerSet(tile, player);
}
}
@Override