From cbd83b5e395b570d5c8c5b87171557b404ee2105 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 10 Jun 2018 23:33:37 -0400 Subject: [PATCH] Fixed many various bugs with syncing things --- .../RemoteMethodAnnotationProcessor.java | 2 +- .../mindustry/content/blocks/DebugBlocks.java | 41 ++++++---- .../anuke/mindustry/core/ContentLoader.java | 16 ++-- .../io/anuke/mindustry/core/NetClient.java | 14 +++- .../io/anuke/mindustry/core/NetServer.java | 8 +- .../io/anuke/mindustry/entities/Player.java | 5 ++ .../src/io/anuke/mindustry/entities/Unit.java | 1 + .../anuke/mindustry/entities/effect/Fire.java | 17 +++-- .../entities/effect/ItemTransfer.java | 1 + .../mindustry/entities/effect/Puddle.java | 74 +++++++++++-------- .../mindustry/entities/traits/SaveTrait.java | 2 +- .../mindustry/entities/traits/SyncTrait.java | 25 +------ .../mindustry/entities/traits/TypeTrait.java | 28 +++++++ .../mindustry/entities/units/BaseUnit.java | 7 +- .../mindustry/graphics/OverlayRenderer.java | 3 +- .../anuke/mindustry/io/SaveFileVersion.java | 21 ------ core/src/io/anuke/mindustry/io/TypeIO.java | 10 +++ .../anuke/mindustry/io/versions/Save16.java | 10 +-- core/src/io/anuke/mindustry/net/Packets.java | 9 ++- kryonet/src/io/anuke/kryonet/KryoServer.java | 1 + 20 files changed, 175 insertions(+), 120 deletions(-) create mode 100644 core/src/io/anuke/mindustry/entities/traits/TypeTrait.java diff --git a/annotations/src/io/anuke/annotations/RemoteMethodAnnotationProcessor.java b/annotations/src/io/anuke/annotations/RemoteMethodAnnotationProcessor.java index 6fbab08303..767441a48a 100644 --- a/annotations/src/io/anuke/annotations/RemoteMethodAnnotationProcessor.java +++ b/annotations/src/io/anuke/annotations/RemoteMethodAnnotationProcessor.java @@ -30,7 +30,7 @@ import java.util.stream.Collectors; }) public class RemoteMethodAnnotationProcessor extends AbstractProcessor { /**Maximum size of each event packet.*/ - public static final int maxPacketSize = 1024; + public static final int maxPacketSize = 4096; /**Name of the base package to put all the generated classes.*/ private static final String packageName = "io.anuke.mindustry.gen"; diff --git a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java index 0bb9ca9f56..89fcff4119 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java @@ -1,8 +1,13 @@ package io.anuke.mindustry.content.blocks; import com.badlogic.gdx.utils.Array; +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; import io.anuke.mindustry.content.Liquids; +import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.gen.CallBlocks; +import io.anuke.mindustry.net.In; import io.anuke.mindustry.type.ContentList; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Liquid; @@ -105,7 +110,7 @@ public class DebugBlocks extends BlockList implements ContentList{ if (i == 0) continue; final int f = i; ImageButton button = cont.addImageButton("white", "toggle", 24, () -> { - entity.source = items.get(f); + CallBlocks.setLiquidSourceLiquid(null, tile, items.get(f)); }).size(38, 42).padBottom(-5.1f).group(group).get(); button.getStyle().imageUpColor = items.get(i).color; button.setChecked(entity.source.id == f); @@ -122,20 +127,6 @@ public class DebugBlocks extends BlockList implements ContentList{ public TileEntity getEntity() { return new LiquidSourceEntity(); } - - class LiquidSourceEntity extends TileEntity { - public Liquid source = Liquids.water; - - @Override - public void write(DataOutputStream stream) throws IOException { - stream.writeByte(source.id); - } - - @Override - public void read(DataInputStream stream) throws IOException { - source = Liquid.getByID(stream.readByte()); - } - } }; itemVoid = new Block("itemvoid") { @@ -153,4 +144,24 @@ public class DebugBlocks extends BlockList implements ContentList{ } }; } + + @Remote(targets = Loc.both, called = Loc.both, in = In.blocks, forward = true) + public static void setLiquidSourceLiquid(Player player, Tile tile, Liquid liquid){ + LiquidSourceEntity entity = tile.entity(); + entity.source = liquid; + } + + class LiquidSourceEntity extends TileEntity { + public Liquid source = Liquids.water; + + @Override + public void write(DataOutputStream stream) throws IOException { + stream.writeByte(source.id); + } + + @Override + public void read(DataInputStream stream) throws IOException { + source = Liquid.getByID(stream.readByte()); + } + } } diff --git a/core/src/io/anuke/mindustry/core/ContentLoader.java b/core/src/io/anuke/mindustry/core/ContentLoader.java index df071e0614..b98a032321 100644 --- a/core/src/io/anuke/mindustry/core/ContentLoader.java +++ b/core/src/io/anuke/mindustry/core/ContentLoader.java @@ -12,7 +12,7 @@ import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.entities.effect.Fire; import io.anuke.mindustry.entities.effect.ItemDrop; import io.anuke.mindustry.entities.effect.Puddle; -import io.anuke.mindustry.entities.traits.SyncTrait; +import io.anuke.mindustry.entities.traits.TypeTrait; import io.anuke.mindustry.entities.units.UnitType; import io.anuke.mindustry.entities.units.types.Drone; import io.anuke.mindustry.entities.units.types.Scout; @@ -130,12 +130,12 @@ public class ContentLoader { /**Registers sync IDs for all types of sync entities.*/ private static void registerTypes(){ - Player.typeID = SyncTrait.registerType(Player::new); - Drone.typeID = SyncTrait.registerType(Drone::new); - Vtol.typeID = SyncTrait.registerType(Vtol::new); - Scout.typeID = SyncTrait.registerType(Scout::new); - ItemDrop.typeID = SyncTrait.registerType(ItemDrop::new); - Fire.typeID = SyncTrait.registerType(Fire::new); - Puddle.typeID = SyncTrait.registerType(Puddle::new); + Player.typeID = TypeTrait.registerType(Player::new); + Drone.typeID = TypeTrait.registerType(Drone::new); + Vtol.typeID = TypeTrait.registerType(Vtol::new); + Scout.typeID = TypeTrait.registerType(Scout::new); + ItemDrop.typeID = TypeTrait.registerType(ItemDrop::new); + Fire.typeID = TypeTrait.registerType(Fire::new); + Puddle.typeID = TypeTrait.registerType(Puddle::new); } } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 006fd88c36..3d146fa207 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -7,6 +7,7 @@ import io.anuke.annotations.Annotations.Variant; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.traits.SyncTrait; +import io.anuke.mindustry.entities.traits.TypeTrait; import io.anuke.mindustry.gen.Call; import io.anuke.mindustry.gen.RemoteReadClient; import io.anuke.mindustry.net.Net; @@ -23,7 +24,6 @@ import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Timer; import java.io.DataInputStream; -import java.io.IOException; import java.util.Arrays; import static io.anuke.mindustry.Vars.*; @@ -228,6 +228,7 @@ public class NetClient extends Module { //go through each entity for (int j = 0; j < amount; j++) { + int position = netClient.byteStream.position(); //save position to check read/write correctness int id = input.readInt(); byte typeID = input.readByte(); @@ -236,7 +237,7 @@ public class NetClient extends Module { //entity must not be added yet, so create it if(entity == null){ - entity = SyncTrait.getTypeByID(typeID).get(); //create entity from supplier + entity = (SyncTrait) TypeTrait.getTypeByID(typeID).get(); //create entity from supplier entity.resetID(id); add = true; } @@ -244,6 +245,11 @@ public class NetClient extends Module { //read the entity entity.read(input, timestamp); + byte readLength = input.readByte(); + if(netClient.byteStream.position() - position - 1 != readLength){ + throw new RuntimeException("Error reading entity of type '"+ group.getType() + "': Read length mismatch [write=" + readLength + ", read=" + (netClient.byteStream.position() - position - 1)+ "]"); + } + if(add){ entity.add(); } @@ -253,8 +259,8 @@ public class NetClient extends Module { //confirm that snapshot has been recieved netClient.lastSnapshotID = snapshotID; - }catch (IOException e){ - e.printStackTrace(); + }catch (Exception e){ + throw new RuntimeException(e); } } } \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index d127751395..ed1c2fc51a 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -21,6 +21,7 @@ import io.anuke.ucore.core.Timers; import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.trait.Entity; +import io.anuke.ucore.io.CountableByteArrayOutputStream; import io.anuke.ucore.io.delta.ByteDeltaEncoder; import io.anuke.ucore.io.delta.ByteMatcherHash; import io.anuke.ucore.io.delta.DEZEncoder; @@ -45,7 +46,7 @@ public class NetServer extends Module{ private boolean closing = false; /**Stream for writing player sync data to.*/ - private ByteArrayOutputStream syncStream = new ByteArrayOutputStream(); + private CountableByteArrayOutputStream syncStream = new CountableByteArrayOutputStream(); /**Data stream for writing player sync data to.*/ private DataOutputStream dataStream = new DataOutputStream(syncStream); /**Encoder for computing snapshot deltas.*/ @@ -144,6 +145,7 @@ public class NetServer extends Module{ 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); + 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; }); @@ -264,10 +266,14 @@ public class NetServer extends Module{ dataStream.writeShort(group.size()); for(Entity entity : group.all()){ + int position = syncStream.position(); //write all entities now dataStream.writeInt(entity.getID()); //write id dataStream.writeByte(((SyncTrait)entity).getTypeID()); //write type ID ((SyncTrait)entity).write(dataStream); //write entity + int length = syncStream.position() - position; //length must always be less than 127 bytes + if(length > 127) throw new RuntimeException("Write size for entity of type " + group.getType() + " must not exceed 127!"); + dataStream.writeByte(length); } } diff --git a/core/src/io/anuke/mindustry/entities/Player.java b/core/src/io/anuke/mindustry/entities/Player.java index 1672a0da2e..785203c2a6 100644 --- a/core/src/io/anuke/mindustry/entities/Player.java +++ b/core/src/io/anuke/mindustry/entities/Player.java @@ -189,12 +189,16 @@ public class Player extends Unit implements BuilderTrait, CarryTrait { @Remote(in = In.entities, targets = Loc.server, called = Loc.server) public static void onPlayerDamage(Player player, float amount){ + if(player == null) return; + player.hitTime = hitDuration; player.health -= amount; } @Remote(in = In.entities, targets = Loc.server, called = Loc.server) public static void onPlayerDeath(Player player){ + if(player == null) return; + player.dead = true; player.respawning = false; player.placeQueue.clear(); @@ -393,6 +397,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait { if(!isLocal){ interpolate(); updateBuilding(this); //building happens even with non-locals + status.update(this); //status effect updating also happens with non locals for effect purposes return; } diff --git a/core/src/io/anuke/mindustry/entities/Unit.java b/core/src/io/anuke/mindustry/entities/Unit.java index 67b14bc9cd..03ae5cc706 100644 --- a/core/src/io/anuke/mindustry/entities/Unit.java +++ b/core/src/io/anuke/mindustry/entities/Unit.java @@ -163,6 +163,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ return (Floor)(tile == null || (tile.floor() == null) ? Blocks.defaultFloor : tile.floor()); } + /**Updates velocity and status effects.*/ public void updateVelocityStatus(float drag, float maxVelocity){ if(isCarried()){ //carried units do not take into account velocity normally set(carrier.getX(), carrier.getY()); diff --git a/core/src/io/anuke/mindustry/entities/effect/Fire.java b/core/src/io/anuke/mindustry/entities/effect/Fire.java index b5cddae517..96b71bf15b 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Fire.java +++ b/core/src/io/anuke/mindustry/entities/effect/Fire.java @@ -52,6 +52,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable fire = Pools.obtain(Fire.class); fire.tile = tile; fire.lifetime = baseLifetime; + fire.set(tile.worldx(), tile.worldy()); fire.add(); map.put(tile.packedPosition(), fire); }else{ @@ -72,7 +73,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable @Override public int getTypeID() { - return 0; + return typeID; } @Override @@ -83,11 +84,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable @Override public void update() { if(Mathf.chance(0.1 * Timers.delta())) { - Effects.effect(EnvironmentFx.fire, tile.worldx() + Mathf.range(4f), tile.worldy() + Mathf.range(4f)); + Effects.effect(EnvironmentFx.fire, x + Mathf.range(4f), y + Mathf.range(4f)); } if(Mathf.chance(0.05 * Timers.delta())){ - Effects.effect(EnvironmentFx.smoke, tile.worldx() + Mathf.range(4f), tile.worldy() + Mathf.range(4f)); + Effects.effect(EnvironmentFx.smoke, x + Mathf.range(4f), y + Mathf.range(4f)); } if(Net.client()){ @@ -136,7 +137,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable if(damage){ entity.damage(0.4f); } - Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f, unit -> unit.applyEffect(StatusEffects.burning, 0.8f)); + Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f, unit -> { + if(!unit.isFlying()) { + unit.applyEffect(StatusEffects.burning, 0.8f); + } + }); } } @@ -186,7 +191,9 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable @Override public void removed() { - map.remove(tile.packedPosition()); + if(tile != null){ + map.remove(tile.packedPosition()); + } reset(); } diff --git a/core/src/io/anuke/mindustry/entities/effect/ItemTransfer.java b/core/src/io/anuke/mindustry/entities/effect/ItemTransfer.java index 0253dad44d..c8eaebc72f 100644 --- a/core/src/io/anuke/mindustry/entities/effect/ItemTransfer.java +++ b/core/src/io/anuke/mindustry/entities/effect/ItemTransfer.java @@ -32,6 +32,7 @@ public class ItemTransfer extends TimedEntity implements DrawTrait{ @Remote(in = In.entities, called = Loc.server, unreliable = true) public static void transferAmmo(Item item, float x, float y, Unit to){ + if(to == null) return; to.addAmmo(item); create(item, x, y, to, () -> {}); } diff --git a/core/src/io/anuke/mindustry/entities/effect/Puddle.java b/core/src/io/anuke/mindustry/entities/effect/Puddle.java index 2a643ec178..6b31e50efe 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Puddle.java +++ b/core/src/io/anuke/mindustry/entities/effect/Puddle.java @@ -37,7 +37,8 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.Vars.puddleGroup; +import static io.anuke.mindustry.Vars.world; public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait { private static final IntMap map = new IntMap<>(); @@ -52,6 +53,7 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait private int loadedPosition = -1; + private float updateTime; private Tile tile; private Liquid liquid; private float amount, targetAmount; @@ -145,14 +147,46 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait @Override public void update() { - if(amount >= maxLiquid/2f && Timers.get(this, "update", 20)){ + + //no updating happens clientside + if(Net.client()){ + amount = Mathf.lerpDelta(amount, targetAmount, 0.15f); + }else{ + //update code + float addSpeed = accepting > 0 ? 3f : 0f; + + amount -= Timers.delta() * (1f - liquid.viscosity) / (5f + addSpeed); + + amount += accepting; + accepting = 0f; + + if (amount >= maxLiquid / 1.5f && generation < maxGeneration) { + float deposited = Math.min((amount - maxLiquid / 1.5f) / 4f, 0.3f) * Timers.delta(); + for (GridPoint2 point : Geometry.d4) { + Tile other = world.tile(tile.x + point.x, tile.y + point.y); + if (other.block() == Blocks.air) { + deposit(other, tile, liquid, deposited, generation + 1); + amount -= deposited / 4f; + } + } + } + + amount = Mathf.clamp(amount, 0, maxLiquid); + + if (amount <= 0f) { + CallEntity.onPuddleRemoved(getID()); + } + } + + //effects-only code + if(amount >= maxLiquid/2f && updateTime <= 0f){ Units.getNearby(rect.setSize(Mathf.clamp(amount/(maxLiquid/1.5f))*10f).setCenter(x, y), unit -> { unit.getHitbox(rect2); if(!rect.overlaps(rect2)) return; unit.applyEffect(liquid.effect, 0.5f); - if(unit.getVelocity().len() > 0.4) { + if(unit.getVelocity().len() > 0.1) { Effects.effect(BlockFx.ripple, liquid.color, unit.x, unit.y); } }); @@ -160,37 +194,11 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait if(liquid.temperature > 0.7f && tile.entity != null && Mathf.chance(0.3 * Timers.delta())){ Fire.create(tile); } + + updateTime = 20f; } - //no updating happens clientside - if(Net.client()){ - amount = Mathf.lerpDelta(amount, targetAmount, 0.15f); - return; - } - - float addSpeed = accepting > 0 ? 3f : 0f; - - amount -= Timers.delta() * (1f - liquid.viscosity) /(5f+addSpeed); - - amount += accepting; - accepting = 0f; - - if(amount >= maxLiquid/1.5f && generation < maxGeneration){ - float deposited = Math.min((amount - maxLiquid/1.5f)/4f, 0.3f) * Timers.delta(); - for(GridPoint2 point : Geometry.d4){ - Tile other = world.tile(tile.x + point.x, tile.y + point.y); - if(other.block() == Blocks.air){ - deposit(other, tile, liquid, deposited, generation + 1); - amount -= deposited/4f; - } - } - } - - amount = Mathf.clamp(amount, 0, maxLiquid); - - if(amount <= 0f){ - CallEntity.onPuddleRemoved(getID()); - } + updateTime -= Timers.delta(); } @Override @@ -282,6 +290,8 @@ public class Puddle extends BaseEntity implements SaveTrait, Poolable, DrawTrait liquid = Liquid.getByID(data.readByte()); targetAmount = data.readShort()/4f; tile = world.tile(data.readInt()); + + map.put(tile.packedPosition(), this); } @Override diff --git a/core/src/io/anuke/mindustry/entities/traits/SaveTrait.java b/core/src/io/anuke/mindustry/entities/traits/SaveTrait.java index 09a7e72e2e..59652182dd 100644 --- a/core/src/io/anuke/mindustry/entities/traits/SaveTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/SaveTrait.java @@ -7,7 +7,7 @@ import java.io.DataOutput; import java.io.IOException; /**Marks an entity as serializable.*/ -public interface SaveTrait extends Entity{ +public interface SaveTrait extends Entity, TypeTrait{ void writeSave(DataOutput stream) throws IOException; void readSave(DataInput stream) throws IOException; } diff --git a/core/src/io/anuke/mindustry/entities/traits/SyncTrait.java b/core/src/io/anuke/mindustry/entities/traits/SyncTrait.java index 591505af32..e1ee55d364 100644 --- a/core/src/io/anuke/mindustry/entities/traits/SyncTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/SyncTrait.java @@ -1,10 +1,8 @@ package io.anuke.mindustry.entities.traits; import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.net.Interpolator; import io.anuke.ucore.entities.trait.Entity; -import io.anuke.ucore.function.Supplier; import java.io.DataInput; import java.io.DataOutput; @@ -12,25 +10,7 @@ import java.io.IOException; import static io.anuke.mindustry.Vars.threads; -public interface SyncTrait extends Entity { - int[] lastRegisteredID = {0}; - Array> registeredTypes = new Array<>(); - - /**Register and return a type ID. The supplier should return a fresh instace of that type.*/ - static int registerType(Supplier supplier){ - registeredTypes.add(supplier); - int result = lastRegisteredID[0]; - lastRegisteredID[0] ++; - return result; - } - - /**Registers a syncable type by ID.*/ - static Supplier getTypeByID(int id){ - if(id == -1){ - throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?"); - } - return registeredTypes.get(id); - } +public interface SyncTrait extends Entity, TypeTrait { /**Whether smoothing of entities is enabled; not yet implemented.*/ static boolean isSmoothing(){ @@ -64,9 +44,6 @@ public interface SyncTrait extends Entity { return null; } - /**Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX.*/ - int getTypeID(); - //Read and write sync data, usually position void write(DataOutput data) throws IOException; void read(DataInput data, long time) throws IOException; diff --git a/core/src/io/anuke/mindustry/entities/traits/TypeTrait.java b/core/src/io/anuke/mindustry/entities/traits/TypeTrait.java new file mode 100644 index 0000000000..443821d119 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/TypeTrait.java @@ -0,0 +1,28 @@ +package io.anuke.mindustry.entities.traits; + +import com.badlogic.gdx.utils.Array; +import io.anuke.ucore.function.Supplier; + +public interface TypeTrait { + int[] lastRegisteredID = {0}; + Array> registeredTypes = new Array<>(); + + /**Register and return a type ID. The supplier should return a fresh instace of that type.*/ + static int registerType(Supplier supplier){ + registeredTypes.add(supplier); + int result = lastRegisteredID[0]; + lastRegisteredID[0] ++; + return result; + } + + /**Registers a syncable type by ID.*/ + static Supplier getTypeByID(int id){ + if(id == -1){ + throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?"); + } + return registeredTypes.get(id); + } + + /**Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX.*/ + int getTypeID(); +} diff --git a/core/src/io/anuke/mindustry/entities/units/BaseUnit.java b/core/src/io/anuke/mindustry/entities/units/BaseUnit.java index 14454a3b4f..afd9198351 100644 --- a/core/src/io/anuke/mindustry/entities/units/BaseUnit.java +++ b/core/src/io/anuke/mindustry/entities/units/BaseUnit.java @@ -266,6 +266,8 @@ public abstract class BaseUnit extends Unit{ @Remote(called = Loc.server, in = In.entities) public static void onUnitShoot(BaseUnit unit, AmmoType type, float rotation){ + if(unit == null) return; + Bullet.create(type.bullet, unit, unit.x + Angles.trnsx(rotation, unit.type.shootTranslation), unit.y + Angles.trnsy(rotation, unit.type.shootTranslation), rotation); @@ -277,11 +279,14 @@ public abstract class BaseUnit extends Unit{ @Remote(called = Loc.server, in = In.entities) public static void onUnitDeath(BaseUnit unit){ + if(unit == null) return; + unit.onSuperDeath(); Effects.effect(ExplosionFx.explosion, unit); Effects.shake(2f, 2f, unit); - unit.remove(); + //must run afterwards so the unit's group is not null + threads.runDelay(unit::remove); } } diff --git a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java index 24a6afa6fa..72735f37e2 100644 --- a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java @@ -30,7 +30,7 @@ public class OverlayRenderer { for(Player player : players) { InputHandler input = control.input(player.playerIndex); - if(!input.isDrawing()) continue; + if(!input.isDrawing() || player.isDead()) continue; Shaders.outline.color.set(Palette.accent); Graphics.beginShaders(Shaders.outline); @@ -44,6 +44,7 @@ public class OverlayRenderer { public void drawTop(){ for(Player player : players) { + if(player.isDead()) continue; //dead player don't draw InputHandler input = control.input(player.playerIndex); diff --git a/core/src/io/anuke/mindustry/io/SaveFileVersion.java b/core/src/io/anuke/mindustry/io/SaveFileVersion.java index 7e961fd79e..1a0d94bde9 100644 --- a/core/src/io/anuke/mindustry/io/SaveFileVersion.java +++ b/core/src/io/anuke/mindustry/io/SaveFileVersion.java @@ -1,9 +1,5 @@ package io.anuke.mindustry.io; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.reflect.ClassReflection; -import com.badlogic.gdx.utils.reflect.Constructor; -import com.badlogic.gdx.utils.reflect.ReflectionException; import io.anuke.mindustry.game.Difficulty; import java.io.DataInputStream; @@ -11,8 +7,6 @@ import java.io.DataOutputStream; import java.io.IOException; public abstract class SaveFileVersion { - private static final ObjectMap, Constructor> cachedConstructors = new ObjectMap<>(); - public final int version; public SaveFileVersion(int version){ @@ -30,19 +24,4 @@ public abstract class SaveFileVersion { public abstract void read(DataInputStream stream) throws IOException; public abstract void write(DataOutputStream stream) throws IOException; - - protected T construct(Class type){ - try { - if (!cachedConstructors.containsKey(type)) { - Constructor cons = ClassReflection.getDeclaredConstructor(type); - cons.setAccessible(true); - cachedConstructors.put(type, cons); - } - - return (T)cachedConstructors.get(type).newInstance(); - - }catch (ReflectionException e){ - throw new RuntimeException(e); - } - } } diff --git a/core/src/io/anuke/mindustry/io/TypeIO.java b/core/src/io/anuke/mindustry/io/TypeIO.java index 90be880983..12a068ff2d 100644 --- a/core/src/io/anuke/mindustry/io/TypeIO.java +++ b/core/src/io/anuke/mindustry/io/TypeIO.java @@ -124,6 +124,16 @@ public class TypeIO { return Upgrade.getByID(buffer.get()); } + @WriteClass(Liquid.class) + public static void writeLiquid(ByteBuffer buffer, Liquid liquid){ + buffer.put((byte)liquid.id); + } + + @ReadClass(Liquid.class) + public static Liquid readLiquid(ByteBuffer buffer){ + return Liquid.getByID(buffer.get()); + } + @WriteClass(AmmoType.class) public static void writeAmmo(ByteBuffer buffer, AmmoType type){ buffer.put(type.id); diff --git a/core/src/io/anuke/mindustry/io/versions/Save16.java b/core/src/io/anuke/mindustry/io/versions/Save16.java index 96b314752e..547fded6aa 100644 --- a/core/src/io/anuke/mindustry/io/versions/Save16.java +++ b/core/src/io/anuke/mindustry/io/versions/Save16.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.utils.TimeUtils; import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.StorageBlocks; import io.anuke.mindustry.entities.traits.SaveTrait; +import io.anuke.mindustry.entities.traits.TypeTrait; import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.Team; @@ -69,11 +70,10 @@ public class Save16 extends SaveFileVersion { for (int i = 0; i < groups; i++) { int amount = stream.readInt(); - byte gid = stream.readByte(); - EntityGroup group = Entities.getGroup(gid); for (int j = 0; j < amount; j++) { - Entity entity = construct(group.getType()); - ((SaveTrait)entity).readSave(stream); + byte typeid = stream.readByte(); + SaveTrait trait = (SaveTrait) TypeTrait.getTypeByID(typeid).get(); + trait.readSave(stream); } } @@ -172,8 +172,8 @@ public class Save16 extends SaveFileVersion { for(EntityGroup group : Entities.getAllGroups()){ if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){ stream.writeInt(group.size()); - stream.writeByte(group.getID()); for(Entity entity : group.all()){ + stream.writeByte(((SaveTrait)entity).getTypeID()); ((SaveTrait)entity).writeSave(stream); } } diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 2ead39114d..91f992529d 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -3,10 +3,12 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.utils.TimeUtils; import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.io.Version; import io.anuke.mindustry.net.Packet.ImportantPacket; import io.anuke.mindustry.net.Packet.UnimportantPacket; import io.anuke.ucore.io.IOUtils; +import io.anuke.ucore.util.Mathf; import java.nio.ByteBuffer; @@ -99,7 +101,7 @@ public class Packets { public int snapid; public long timeSent; //player snapshot data - public float x, y, rotation, baseRotation; + public float x, y, rotation, baseRotation, xv, yv; @Override public void write(ByteBuffer buffer) { @@ -111,6 +113,9 @@ public class Packets { buffer.putFloat(player.x); buffer.putFloat(player.y); + + 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)); //saving 4 bytes, yay? buffer.putShort((short)(player.rotation*2)); buffer.putShort((short)(player.baseRotation*2)); @@ -124,6 +129,8 @@ public class Packets { x = buffer.getFloat(); y = buffer.getFloat(); + xv = buffer.get() / Unit.velocityPercision; + yv = buffer.get() / Unit.velocityPercision; rotation = buffer.getShort()/2f; baseRotation = buffer.getShort()/2f; } diff --git a/kryonet/src/io/anuke/kryonet/KryoServer.java b/kryonet/src/io/anuke/kryonet/KryoServer.java index 8cc2dd66a9..53a4428472 100644 --- a/kryonet/src/io/anuke/kryonet/KryoServer.java +++ b/kryonet/src/io/anuke/kryonet/KryoServer.java @@ -159,6 +159,7 @@ public class KryoServer implements ServerProvider { @Override public void close() { + UCore.setPrivate(server, "shutdown", true); connections.clear(); lastconnection = 0;