Fixed many various bugs with syncing things

This commit is contained in:
Anuken 2018-06-10 23:33:37 -04:00
parent 2ddd768393
commit cbd83b5e39
20 changed files with 175 additions and 120 deletions

View File

@ -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";

View File

@ -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());
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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());

View File

@ -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();
}

View File

@ -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, () -> {});
}

View File

@ -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<Puddle> 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

View File

@ -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;
}

View File

@ -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<Supplier<? extends SyncTrait>> registeredTypes = new Array<>();
/**Register and return a type ID. The supplier should return a fresh instace of that type.*/
static int registerType(Supplier<? extends SyncTrait> supplier){
registeredTypes.add(supplier);
int result = lastRegisteredID[0];
lastRegisteredID[0] ++;
return result;
}
/**Registers a syncable type by ID.*/
static Supplier<? extends SyncTrait> 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;

View File

@ -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<Supplier<? extends TypeTrait>> registeredTypes = new Array<>();
/**Register and return a type ID. The supplier should return a fresh instace of that type.*/
static int registerType(Supplier<? extends TypeTrait> supplier){
registeredTypes.add(supplier);
int result = lastRegisteredID[0];
lastRegisteredID[0] ++;
return result;
}
/**Registers a syncable type by ID.*/
static Supplier<? extends TypeTrait> 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();
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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<Class<?>, 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> T construct(Class<T> 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);
}
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -159,6 +159,7 @@ public class KryoServer implements ServerProvider {
@Override
public void close() {
UCore.setPrivate(server, "shutdown", true);
connections.clear();
lastconnection = 0;