Multiplayer bugfixes

This commit is contained in:
Anuken
2020-07-09 13:13:57 -04:00
parent 29e9d064df
commit 2584197a02
24 changed files with 139 additions and 70 deletions

View File

@@ -193,7 +193,7 @@ public class EntityIO{
st("$L = $L($T.$L($L, $L, alpha))", name, field.annotation(SyncField.class).clamped() ? "arc.math.Mathf.clamp" : "", Mathf.class, field.annotation(SyncField.class).value() ? "lerp" : "slerp", lastName, targetName); st("$L = $L($T.$L($L, $L, alpha))", name, field.annotation(SyncField.class).clamped() ? "arc.math.Mathf.clamp" : "", Mathf.class, field.annotation(SyncField.class).value() ? "lerp" : "slerp", lastName, targetName);
} }
ncont("else"); //no meaningful data has arrived yet ncont("else if(lastUpdated != 0)"); //check if no meaningful data has arrived yet
//write values directly to targets //write values directly to targets
for(Svar field : fields){ for(Svar field : fields){

View File

@@ -566,6 +566,14 @@ public class EntityProcess extends BaseProcessor{
//write the groups //write the groups
groupsBuilder.addMethod(groupInit.build()); groupsBuilder.addMethod(groupInit.build());
MethodSpec.Builder groupClear = MethodSpec.methodBuilder("clear").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
for(GroupDefinition group : groupDefs){
groupClear.addStatement("$L.clear()", group.name);
}
//write clear
groupsBuilder.addMethod(groupClear.build());
//add method for resizing all necessary groups //add method for resizing all necessary groups
MethodSpec.Builder groupResize = MethodSpec.methodBuilder("resize") MethodSpec.Builder groupResize = MethodSpec.methodBuilder("resize")
.addParameter(TypeName.FLOAT, "x").addParameter(TypeName.FLOAT, "y").addParameter(TypeName.FLOAT, "w").addParameter(TypeName.FLOAT, "h") .addParameter(TypeName.FLOAT, "x").addParameter(TypeName.FLOAT, "y").addParameter(TypeName.FLOAT, "w").addParameter(TypeName.FLOAT, "h")

View File

@@ -163,7 +163,7 @@ public class Logic implements ApplicationListener{
//fire change event, since it was technically changed //fire change event, since it was technically changed
Events.fire(new StateChangeEvent(prev, State.menu)); Events.fire(new StateChangeEvent(prev, State.menu));
Groups.all.clear(); Groups.clear();
Time.clear(); Time.clear();
Events.fire(new ResetEvent()); Events.fire(new ResetEvent());

View File

@@ -343,7 +343,7 @@ public class NetClient implements ApplicationListener{
@Remote(variants = Variant.both) @Remote(variants = Variant.both)
public static void worldDataBegin(){ public static void worldDataBegin(){
Groups.all.clear(); Groups.clear();
netClient.removed.clear(); netClient.removed.clear();
logic.reset(); logic.reset();
@@ -361,6 +361,7 @@ public class NetClient implements ApplicationListener{
@Remote(variants = Variant.one) @Remote(variants = Variant.one)
public static void setPosition(float x, float y){ public static void setPosition(float x, float y){
player.unit().set(x, y);
player.set(x, y); player.set(x, y);
} }
@@ -517,7 +518,7 @@ public class NetClient implements ApplicationListener{
quiet = false; quiet = false;
lastSent = 0; lastSent = 0;
Groups.all.clear(); Groups.clear();
ui.chatfrag.clearMessages(); ui.chatfrag.clearMessages();
} }
@@ -566,13 +567,14 @@ public class NetClient implements ApplicationListener{
Unit unit = player.dead() ? Nulls.unit : player.unit(); Unit unit = player.dead() ? Nulls.unit : player.unit();
Call.clientShapshot(lastSent++, Call.clientShapshot(lastSent++,
player.dead(),
unit.x, unit.y, unit.x, unit.y,
player.unit().aimX(), player.unit().aimY(), player.unit().aimX(), player.unit().aimY(),
unit.rotation, unit.rotation,
unit instanceof Mechc ? ((Mechc)unit).baseRotation() : 0, unit instanceof Mechc ? ((Mechc)unit).baseRotation() : 0,
unit.vel.x, unit.vel.y, unit.vel.x, unit.vel.y,
player.miner().mineTile(), player.miner().mineTile(),
control.input.isBoosting, control.input.isShooting, ui.chatfrag.shown(), player.boosting, player.shooting, ui.chatfrag.shown(),
requests, requests,
Core.camera.position.x, Core.camera.position.y, Core.camera.position.x, Core.camera.position.y,
Core.camera.width * viewScale, Core.camera.height * viewScale); Core.camera.width * viewScale, Core.camera.height * viewScale);

View File

@@ -529,6 +529,7 @@ public class NetServer implements ApplicationListener{
public static void clientShapshot( public static void clientShapshot(
Player player, Player player,
int snapshotID, int snapshotID,
boolean dead,
float x, float y, float x, float y,
float pointerX, float pointerY, float pointerX, float pointerY,
float rotation, float baseRotation, float rotation, float baseRotation,
@@ -538,23 +539,24 @@ public class NetServer implements ApplicationListener{
@Nullable BuildPlan[] requests, @Nullable BuildPlan[] requests,
float viewX, float viewY, float viewWidth, float viewHeight float viewX, float viewY, float viewWidth, float viewHeight
){ ){
NetConnection connection = player.con; NetConnection con = player.con;
if(connection == null || snapshotID < connection.lastRecievedClientSnapshot) return; if(con == null || snapshotID < con.lastRecievedClientSnapshot) return;
boolean verifyPosition = !player.dead() && netServer.admins.getStrict() && headless; boolean verifyPosition = !player.dead() && netServer.admins.getStrict() && headless;
if(connection.lastRecievedClientTime == 0) connection.lastRecievedClientTime = Time.millis() - 16; if(con.lastRecievedClientTime == 0) con.lastRecievedClientTime = Time.millis() - 16;
connection.viewX = viewX; con.viewX = viewX;
connection.viewY = viewY; con.viewY = viewY;
connection.viewWidth = viewWidth; con.viewWidth = viewWidth;
connection.viewHeight = viewHeight; con.viewHeight = viewHeight;
//disable shooting when a mech flies //disable shooting when a mech flies
if(!player.dead() && player.unit().isFlying() && player.unit() instanceof Mechc){ if(!player.dead() && player.unit().isFlying() && player.unit() instanceof Mechc){
shooting = false; shooting = false;
} }
//TODO these need to be assigned elsewhere
player.mouseX = pointerX; player.mouseX = pointerX;
player.mouseY = pointerY; player.mouseY = pointerY;
player.typing = chatting; player.typing = chatting;
@@ -582,7 +584,7 @@ public class NetServer implements ApplicationListener{
continue; continue;
}else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){ }else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){
continue; continue;
}else if(connection.rejectedRequests.contains(r -> r.breaking == req.breaking && r.x == req.x && r.y == req.y)){ //check if request was recently rejected, and skip it if so }else if(con.rejectedRequests.contains(r -> r.breaking == req.breaking && r.x == req.x && r.y == req.y)){ //check if request was recently rejected, and skip it if so
continue; continue;
}else if(!netServer.admins.allowAction(player, req.breaking ? ActionType.breakBlock : ActionType.placeBlock, tile, action -> { //make sure request is allowed by the server }else if(!netServer.admins.allowAction(player, req.breaking ? ActionType.breakBlock : ActionType.placeBlock, tile, action -> { //make sure request is allowed by the server
action.block = req.block; action.block = req.block;
@@ -591,33 +593,39 @@ public class NetServer implements ApplicationListener{
})){ })){
//force the player to remove this request if that's not the case //force the player to remove this request if that's not the case
Call.removeQueueBlock(player.con, req.x, req.y, req.breaking); Call.removeQueueBlock(player.con, req.x, req.y, req.breaking);
connection.rejectedRequests.add(req); con.rejectedRequests.add(req);
continue; continue;
} }
player.builder().plans().addLast(req); player.builder().plans().addLast(req);
} }
} }
connection.rejectedRequests.clear(); con.rejectedRequests.clear();
if(!player.dead()){ if(!player.dead()){
Unit unit = player.unit(); Unit unit = player.unit();
unit.vel().set(xVelocity, yVelocity).limit(unit.type().speed); unit.vel.set(xVelocity, yVelocity).limit(unit.type().speed);
long elapsed = Time.timeSinceMillis(connection.lastRecievedClientTime); long elapsed = Time.timeSinceMillis(con.lastRecievedClientTime);
float maxSpeed = player.dead() ? Float.MAX_VALUE : player.unit().type().speed; float maxSpeed = player.unit().type().speed;
float maxMove = elapsed / 1000f * 60f * maxSpeed * 1.1f; float maxMove = elapsed / 1000f * 60f * maxSpeed * 1.1f;
if(connection.lastUnit != unit && !player.dead()){ if(con.lastUnit != unit){
connection.lastUnit = unit; con.lastUnit = unit;
connection.lastPosition.set(unit); con.lastPosition.set(unit);
} }
vector.set(x, y).sub(connection.lastPosition); //if the player think they're dead their position should be ignored
if(dead){
x = unit.x;
y = unit.y;
}
vector.set(x, y).sub(con.lastPosition);
vector.limit(maxMove); vector.limit(maxMove);
float prevx = unit.x(), prevy = unit.y(); float prevx = unit.x, prevy = unit.y;
unit.set(connection.lastPosition); unit.set(con.lastPosition);
if(!unit.isFlying()){ if(!unit.isFlying()){
unit.move(vector.x, vector.y); unit.move(vector.x, vector.y);
}else{ }else{
@@ -625,30 +633,24 @@ public class NetServer implements ApplicationListener{
} }
//set last position after movement //set last position after movement
connection.lastPosition.set(unit); con.lastPosition.set(unit);
float newx = unit.x(), newy = unit.y(); float newx = unit.x, newy = unit.y;
if(!verifyPosition){ if(!verifyPosition){
unit.set(prevx, prevy); unit.set(prevx, prevy);
newx = x; newx = x;
newy = y; newy = y;
}else if(!Mathf.within(x, y, newx, newy, correctDist)){ }else if(!Mathf.within(x, y, newx, newy, correctDist) && !dead){
Call.setPosition(player.con, newx, newy); //teleport and correct position when necessary Call.setPosition(player.con, newx, newy); //teleport and correct position when necessary
} }
//reset player to previous synced position so it gets interpolated
//the server does not interpolate
if(!headless){
unit.set(prevx, prevy);
}
//write sync data to the buffer //write sync data to the buffer
fbuffer.limit(20); fbuffer.limit(20);
fbuffer.position(0); fbuffer.position(0);
//now, put the new position, rotation and baserotation into the buffer so it can be read //now, put the new position, rotation and baserotation into the buffer so it can be read
//TODO this is terrible
if(unit instanceof Mechc) fbuffer.put(baseRotation); //base rotation is optional if(unit instanceof Mechc) fbuffer.put(baseRotation); //base rotation is optional
fbuffer.put(unit.elevation());
fbuffer.put(rotation); //rotation is always there fbuffer.put(rotation); //rotation is always there
fbuffer.put(newx); fbuffer.put(newx);
fbuffer.put(newy); fbuffer.put(newy);
@@ -661,8 +663,8 @@ public class NetServer implements ApplicationListener{
player.y = y; player.y = y;
} }
connection.lastRecievedClientSnapshot = snapshotID; con.lastRecievedClientSnapshot = snapshotID;
connection.lastRecievedClientTime = Time.millis(); con.lastRecievedClientTime = Time.millis();
} }
@Remote(targets = Loc.client, called = Loc.server) @Remote(targets = Loc.client, called = Loc.server)

View File

@@ -166,6 +166,9 @@ public class EntityGroup<T extends Entityc> implements Iterable<T>{
int idx = array.indexOf(type, true); int idx = array.indexOf(type, true);
if(idx != -1){ if(idx != -1){
array.remove(idx); array.remove(idx);
if(map != null){
map.remove(type.id());
}
//fix iteration index when removing //fix iteration index when removing
if(index >= idx){ if(index >= idx){

View File

@@ -4,7 +4,7 @@ import mindustry.annotations.Annotations.*;
import mindustry.gen.*; import mindustry.gen.*;
class GroupDefs<G>{ class GroupDefs<G>{
@GroupDef(value = Entityc.class, mapping = true) G all; @GroupDef(value = Entityc.class) G all;
@GroupDef(value = Playerc.class, mapping = true) G player; @GroupDef(value = Playerc.class, mapping = true) G player;
@GroupDef(value = Bulletc.class, spatial = true, collide = true) G bullet; @GroupDef(value = Bulletc.class, spatial = true, collide = true) G bullet;
@GroupDef(value = Unitc.class, spatial = true, mapping = true) G unit; @GroupDef(value = Unitc.class, spatial = true, mapping = true) G unit;

View File

@@ -3,6 +3,7 @@ package mindustry.entities;
import arc.func.*; import arc.func.*;
import arc.math.geom.*; import arc.math.geom.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.world.*; import mindustry.world.*;
@@ -21,6 +22,12 @@ public class Units{
unit.killed(); unit.killed();
} }
@Remote(called = Loc.server)
public static void unitDespawn(Unit unit){
Fx.unitDespawn.at(unit.x, unit.y, 0, unit);
unit.remove();
}
/** @return whether a new instance of a unit of this team can be created. */ /** @return whether a new instance of a unit of this team can be created. */
public static boolean canCreate(Team team){ public static boolean canCreate(Team team){
return teamIndex.count(team) < getCap(team); return teamIndex.count(team) < getCap(team);

View File

@@ -1,10 +1,12 @@
package mindustry.entities.comp; package mindustry.entities.comp;
import arc.graphics.g2d.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.ui.*;
import static mindustry.Vars.tilesize; import static mindustry.Vars.*;
@Component @Component
abstract class BlockUnitComp implements Unitc{ abstract class BlockUnitComp implements Unitc{
@@ -29,6 +31,12 @@ abstract class BlockUnitComp implements Unitc{
} }
} }
@Replace
@Override
public TextureRegion icon(){
return tile.block.icon(Cicon.full);
}
@Override @Override
public void killed(){ public void killed(){
tile.kill(); tile.kill();

View File

@@ -28,7 +28,7 @@ abstract class BuilderComp implements Unitc{
@Import float x, y, rotation; @Import float x, y, rotation;
Queue<BuildPlan> plans = new Queue<>(); @SyncLocal Queue<BuildPlan> plans = new Queue<>();
transient boolean building = true; transient boolean building = true;
@Override @Override

View File

@@ -19,7 +19,7 @@ abstract class DecalComp implements Drawc, Timedc, Rotc, Posc{
public void draw(){ public void draw(){
Draw.z(Layer.scorch); Draw.z(Layer.scorch);
Draw.mixcol(color, 1f); Draw.mixcol(color, color.a);
Draw.alpha(1f - Mathf.curve(fin(), 0.98f)); Draw.alpha(1f - Mathf.curve(fin(), 0.98f));
Draw.rect(region, x, y, rotation); Draw.rect(region, x, y, rotation);
Draw.reset(); Draw.reset();

View File

@@ -32,6 +32,10 @@ abstract class EntityComp{
return ((Object)this) == player || ((Object)this) instanceof Unitc && ((Unitc)((Object)this)).controller() == player; return ((Object)this) == player || ((Object)this) instanceof Unitc && ((Unitc)((Object)this)).controller() == player;
} }
boolean isRemote(){
return ((Object)this) instanceof Unitc && ((Unitc)((Object)this)).isPlayer() && !isLocal();
}
boolean isNull(){ boolean isNull(){
return false; return false;
} }

View File

@@ -17,7 +17,7 @@ abstract class FlyingComp implements Posc, Velc, Healthc, Hitboxc{
@Import float x, y; @Import float x, y;
@Import Vec2 vel; @Import Vec2 vel;
@SyncField(value = true, clamped = true) @SyncLocal float elevation; @SyncLocal float elevation;
private transient boolean wasFlying; private transient boolean wasFlying;
transient float drownTime; transient float drownTime;
transient float splashTimer; transient float splashTimer;

View File

@@ -22,7 +22,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
@Import UnitType type; @Import UnitType type;
transient float mineTimer; transient float mineTimer;
@Nullable Tile mineTile; @Nullable @SyncLocal Tile mineTile;
public boolean canMine(Item item){ public boolean canMine(Item item){
return type.mineTier >= item.hardness; return type.mineTier >= item.hardness;

View File

@@ -12,8 +12,8 @@ import mindustry.annotations.Annotations.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.core.*; import mindustry.core.*;
import mindustry.entities.units.*; import mindustry.entities.units.*;
import mindustry.game.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.net.Administration.*; import mindustry.net.Administration.*;
@@ -37,10 +37,10 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
transient @Nullable NetConnection con; transient @Nullable NetConnection con;
@ReadOnly Team team = Team.sharded; @ReadOnly Team team = Team.sharded;
@SyncLocal boolean admin, typing, shooting, boosting;
@SyncLocal float mouseX, mouseY;
String name = "noname"; String name = "noname";
boolean admin, typing, shooting, boosting;
Color color = new Color(); Color color = new Color();
float mouseX, mouseY;
transient float deathTimer; transient float deathTimer;
transient String lastText = ""; transient String lastText = "";
@@ -63,9 +63,10 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
} }
public TextureRegion icon(){ public TextureRegion icon(){
//display default icon for dead players
if(dead()) return core() == null ? UnitTypes.alpha.icon(Cicon.full) : ((CoreBlock)core().block).unitType.icon(Cicon.full); if(dead()) return core() == null ? UnitTypes.alpha.icon(Cicon.full) : ((CoreBlock)core().block).unitType.icon(Cicon.full);
return unit.type().icon(Cicon.full); return unit.icon();
} }
public void reset(){ public void reset(){
@@ -78,6 +79,11 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
} }
} }
@Override
public boolean isValidController(){
return isAdded();
}
@Replace @Replace
public float clipSize(){ public float clipSize(){
return unit.isNull() ? 20 : unit.type().hitsize * 2f; return unit.isNull() ? 20 : unit.type().hitsize * 2f;
@@ -125,6 +131,14 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
} }
@Override
public void remove(){
//clear unit upon removal
if(!unit.isNull()){
clearUnit();
}
}
public void team(Team team){ public void team(Team team){
this.team = team; this.team = team;
unit.team(team); unit.team(team);

View File

@@ -22,8 +22,19 @@ abstract class SyncComp implements Entityc{
@Override @Override
public void update(){ public void update(){
if(Vars.net.client() && !isLocal()){ //interpolate the player if:
//- this is a client and the entity is everything except the local player
//- this is a server and the entity is a remote player
if((Vars.net.client() && !isLocal()) || isRemote()){
interpolate(); interpolate();
} }
} }
@Override
public void remove(){
//notify client of removal
if(Vars.net.client()){
Vars.netClient.addRemovedEntity(id());
}
}
} }

View File

@@ -1,6 +1,7 @@
package mindustry.entities.comp; package mindustry.entities.comp;
import arc.*; import arc.*;
import arc.graphics.g2d.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
@@ -233,11 +234,15 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
//remove units spawned by the core //remove units spawned by the core
if(spawnedByCore && !isPlayer()){ if(spawnedByCore && !isPlayer()){
Fx.unitDespawn.at(x, y, 0, this); Call.unitDespawn(base());
remove();
} }
} }
/** @return a preview icon for this unit. */
public TextureRegion icon(){
return type.icon(Cicon.full);
}
/** Actually destroys the unit, removing it and creating explosions. **/ /** Actually destroys the unit, removing it and creating explosions. **/
public void destroy(){ public void destroy(){
float explosiveness = 2f + item().explosiveness * stack().amount; float explosiveness = 2f + item().explosiveness * stack().amount;

View File

@@ -6,6 +6,10 @@ public interface UnitController{
void unit(Unit unit); void unit(Unit unit);
Unit unit(); Unit unit();
default boolean isValidController(){
return true;
}
default void command(UnitCommand command){ default void command(UnitCommand command){
} }

View File

@@ -221,7 +221,7 @@ public class DesktopInput extends InputHandler{
} }
if(Core.input.keyRelease(Binding.select)){ if(Core.input.keyRelease(Binding.select)){
isShooting = false; player.shooting = false;
} }
if(state.isGame() && Core.input.keyTap(Binding.minimap) && !scene.hasDialog() && !(scene.getKeyboardFocus() instanceof TextField)){ if(state.isGame() && Core.input.keyTap(Binding.minimap) && !scene.hasDialog() && !(scene.getKeyboardFocus() instanceof TextField)){
@@ -248,8 +248,8 @@ public class DesktopInput extends InputHandler{
mode = none; mode = none;
} }
if(isShooting && !canShoot()){ if(player.shooting && !canShoot()){
isShooting = false; player.shooting = false;
} }
if(isPlacing() && player.isBuilder()){ if(isPlacing() && player.isBuilder()){
@@ -464,10 +464,10 @@ 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(selected.build) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && (player.builder().plans().size == 0 || !player.builder().isBuilding()) && !droppingItem && if(!tileTapped(selected.build) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && (player.builder().plans().size == 0 || !player.builder().isBuilding()) && !droppingItem &&
!tryBeginMine(selected) && player.miner().mineTile() == null && !Core.scene.hasKeyboard()){ !tryBeginMine(selected) && player.miner().mineTile() == null && !Core.scene.hasKeyboard()){
isShooting = shouldShoot; player.shooting = shouldShoot;
} }
}else if(!Core.scene.hasKeyboard()){ //if it's out of bounds, shooting is just fine }else if(!Core.scene.hasKeyboard()){ //if it's out of bounds, shooting is just fine
isShooting = shouldShoot; player.shooting = shouldShoot;
} }
}else if(Core.input.keyTap(Binding.deselect) && isPlacing()){ }else if(Core.input.keyTap(Binding.deselect) && isPlacing()){
block = null; block = null;
@@ -575,7 +575,7 @@ public class DesktopInput extends InputHandler{
movement.set(xa, ya).nor().scl(speed); movement.set(xa, ya).nor().scl(speed);
float mouseAngle = Angles.mouseAngle(unit.x(), unit.y()); float mouseAngle = Angles.mouseAngle(unit.x(), unit.y());
boolean aimCursor = omni && isShooting && unit.type().hasWeapons() && unit.type().faceTarget && !boosted && unit.type().rotateShooting; boolean aimCursor = omni && player.shooting && unit.type().hasWeapons() && unit.type().faceTarget && !boosted && unit.type().rotateShooting;
if(aimCursor){ if(aimCursor){
unit.lookAt(mouseAngle); unit.lookAt(mouseAngle);
@@ -595,10 +595,11 @@ public class DesktopInput extends InputHandler{
} }
unit.aim(unit.type().faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation(), Core.input.mouseWorld().dst(unit)).add(unit.x(), unit.y())); unit.aim(unit.type().faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation(), Core.input.mouseWorld().dst(unit)).add(unit.x(), unit.y()));
unit.controlWeapons(true, isShooting && !boosted); unit.controlWeapons(true, player.shooting && !boosted);
isBoosting = Core.input.keyDown(Binding.boost) && !movement.isZero(); player.boosting = Core.input.keyDown(Binding.boost) && !movement.isZero();
player.boosting(isBoosting); player.mouseX = unit.aimX();
player.mouseY = unit.aimY();
//TODO netsync this //TODO netsync this
if(unit instanceof Payloadc){ if(unit instanceof Payloadc){

View File

@@ -58,7 +58,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public int rotation; public int rotation;
public boolean droppingItem; public boolean droppingItem;
public Group uiGroup; public Group uiGroup;
public boolean isShooting, isBuilding = true, buildWasAutoPaused = false, isBoosting = false; public boolean isBuilding = true, buildWasAutoPaused = false;
public @Nullable UnitType controlledType; public @Nullable UnitType controlledType;
protected @Nullable Schematic lastSchematic; protected @Nullable Schematic lastSchematic;
@@ -249,7 +249,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
} }
public void update(){ public void update(){
player.typing(ui.chatfrag.shown()); player.typing = ui.chatfrag.shown();
if(player.isBuilder()){ if(player.isBuilder()){
player.builder().building(isBuilding); player.builder().building(isBuilding);

View File

@@ -454,7 +454,7 @@ public class MobileInput extends InputHandler implements GestureListener{
lastLineY = tileY; lastLineY = tileY;
}else if(!tryTapPlayer(worldx, worldy) && Core.settings.getBool("keyboard")){ }else if(!tryTapPlayer(worldx, worldy) && Core.settings.getBool("keyboard")){
//shoot on touch down when in keyboard mode //shoot on touch down when in keyboard mode
isShooting = true; player.shooting = true;
} }
} }
@@ -589,11 +589,11 @@ public class MobileInput extends InputHandler implements GestureListener{
if(Core.settings.getBool("keyboard")){ if(Core.settings.getBool("keyboard")){
if(Core.input.keyRelease(Binding.select)){ if(Core.input.keyRelease(Binding.select)){
isShooting = false; player.shooting = false;
} }
if(isShooting && !canShoot()){ if(player.shooting && !canShoot()){
isShooting = false; player.shooting = false;
} }
} }

View File

@@ -170,7 +170,7 @@ public class TypeIO{
} }
public static <T extends Entityc> T readEntity(Reads read){ public static <T extends Entityc> T readEntity(Reads read){
return (T)Groups.all.getByID(read.i()); return (T)Groups.sync.getByID(read.i());
} }
public static void writeBuilding(Writes write, Building tile){ public static void writeBuilding(Writes write, Building tile){

View File

@@ -27,7 +27,7 @@ public class NetworkIO{
stream.writeInt(state.wave); stream.writeInt(state.wave);
stream.writeFloat(state.wavetime); stream.writeFloat(state.wavetime);
stream.writeInt(player.id()); stream.writeInt(player.id);
player.write(Writes.get(stream)); player.write(Writes.get(stream));
SaveIO.getSaveWriter().writeContentHeader(stream); SaveIO.getSaveWriter().writeContentHeader(stream);
@@ -47,11 +47,11 @@ public class NetworkIO{
state.wave = stream.readInt(); state.wave = stream.readInt();
state.wavetime = stream.readFloat(); state.wavetime = stream.readFloat();
Groups.all.clear(); Groups.clear();
int id = stream.readInt(); int id = stream.readInt();
player.reset(); player.reset();
player.read(Reads.get(stream)); player.read(Reads.get(stream));
player.id(id); player.id = id;
player.add(); player.add();
SaveIO.getSaveWriter().readContentHeader(stream); SaveIO.getSaveWriter().readContentHeader(stream);

View File

@@ -59,9 +59,9 @@ public class BuildBlock extends Block{
@Remote(called = Loc.server) @Remote(called = Loc.server)
public static void constructFinish(Tile tile, Block block, int builderID, byte rotation, Team team, boolean skipConfig){ public static void constructFinish(Tile tile, Block block, int builderID, byte rotation, Team team, boolean skipConfig){
if(tile == null) return; if(tile == null) return;
float healthf = tile.build.healthf(); float healthf = tile.build == null ? 1f : tile.build.healthf();
tile.setBlock(block, team, rotation); tile.setBlock(block, team, rotation);
tile.build.health(block.health * healthf); tile.build.health = block.health * healthf;
//last builder was this local client player, call placed() //last builder was this local client player, call placed()
if(!headless && builderID == player.unit().id()){ if(!headless && builderID == player.unit().id()){
if(!skipConfig){ if(!skipConfig){