LZ4 compression

This commit is contained in:
Anuken
2018-09-08 17:07:25 -04:00
parent 8dbdbe6d6c
commit 46a8edb781
10 changed files with 67 additions and 67 deletions
+1
View File
@@ -230,6 +230,7 @@ project(":kryonet") {
dependencies { dependencies {
compile project(":core") compile project(":core")
compile 'com.github.crykn:kryonet:2.22.1' compile 'com.github.crykn:kryonet:2.22.1'
compile "org.lz4:lz4-java:1.4.1"
compile 'com.github.Anuken:WaifUPnP:05eb46bc577fd7674596946ba288c96c0cedd893' compile 'com.github.Anuken:WaifUPnP:05eb46bc577fd7674596946ba288c96c0cedd893'
} }
} }
@@ -190,7 +190,7 @@ public class NetClient extends Module{
} }
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true) @Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
public static void onSnapshot(byte[] chunk, int snapshotID, short chunkID, int totalLength){ public static void onSnapshot(byte[] chunk, int snapshotID, short chunkID, int totalLength, int uncompressedLength){
int totalChunks = Mathf.ceil((float) totalLength / NetServer.maxSnapshotSize); int totalChunks = Mathf.ceil((float) totalLength / NetServer.maxSnapshotSize);
if(NetServer.debugSnapshots) if(NetServer.debugSnapshots)
@@ -234,8 +234,8 @@ public class NetClient extends Module{
if(NetServer.debugSnapshots) if(NetServer.debugSnapshots)
Log.info("Finished recieving snapshot ID {0} length {1}", snapshotID, chunk.length); Log.info("Finished recieving snapshot ID {0} length {1}", snapshotID, chunk.length);
byte[] result = snapshot; byte[] result = Net.decompressSnapshot(snapshot, uncompressedLength);
int length = snapshot.length; int length = result.length;
netClient.lastSnapshotBaseID = snapshotID; netClient.lastSnapshotBaseID = snapshotID;
@@ -228,9 +228,9 @@ public class NetServer extends Module{
} }
/** Sends a raw byte[] snapshot to a client, splitting up into chunks when needed.*/ /** Sends a raw byte[] snapshot to a client, splitting up into chunks when needed.*/
private static void sendSplitSnapshot(int userid, byte[] bytes, int snapshotID){ private static void sendSplitSnapshot(int userid, byte[] bytes, int snapshotID, int uncompressedLength){
if(bytes.length < maxSnapshotSize){ if(bytes.length < maxSnapshotSize){
scheduleSnapshot(() -> Call.onSnapshot(userid, bytes, snapshotID, (short) 0, bytes.length)); scheduleSnapshot(() -> Call.onSnapshot(userid, bytes, snapshotID, (short) 0, bytes.length, uncompressedLength));
}else{ }else{
int remaining = bytes.length; int remaining = bytes.length;
int offset = 0; int offset = 0;
@@ -247,7 +247,7 @@ public class NetServer extends Module{
} }
short fchunk = (short)chunkid; short fchunk = (short)chunkid;
scheduleSnapshot(() -> Call.onSnapshot(userid, toSend, snapshotID, fchunk, bytes.length)); scheduleSnapshot(() -> Call.onSnapshot(userid, toSend, snapshotID, fchunk, bytes.length, uncompressedLength));
remaining -= used; remaining -= used;
offset += used; offset += used;
@@ -608,10 +608,12 @@ public class NetServer extends Module{
dataStream.close(); dataStream.close();
byte[] bytes = syncStream.toByteArray(); byte[] bytes = syncStream.toByteArray();
int uncompressed = bytes.length;
bytes = Net.compressSnapshot(bytes);
int snapid = connection.lastSentSnapshotID ++; int snapid = connection.lastSentSnapshotID ++;
if(debugSnapshots) Log.info("Sent snapshot: {0} bytes.", bytes.length); if(debugSnapshots) Log.info("Sent snapshot: {0} bytes.", bytes.length);
sendSplitSnapshot(connection.id, bytes, snapid); sendSplitSnapshot(connection.id, bytes, snapid, uncompressed);
} }
}catch(IOException e){ }catch(IOException e){
+32 -54
View File
@@ -119,6 +119,14 @@ public class Net{
active = false; active = false;
} }
public static byte[] compressSnapshot(byte[] input){
return serverProvider.compressSnapshot(input);
}
public static byte[] decompressSnapshot(byte[] input, int size){
return clientProvider.decompressSnapshot(input, size);
}
/** /**
* Starts discovering servers on a different thread. Does not work with GWT. * Starts discovering servers on a different thread. Does not work with GWT.
* Callback is run on the main libGDX thread. * Callback is run on the main libGDX thread.
@@ -337,99 +345,69 @@ public class Net{
tcp, udp tcp, udp
} }
/** /**Client implementation.*/
* Client implementation.
*/
public interface ClientProvider{ public interface ClientProvider{
/** /**Connect to a server.*/
* Connect to a server.
*/
void connect(String ip, int port) throws IOException; void connect(String ip, int port) throws IOException;
/** /**Send an object to the server.*/
* Send an object to the server.
*/
void send(Object object, SendMode mode); void send(Object object, SendMode mode);
/** /**Update the ping. Should be done every second or so.*/
* Update the ping. Should be done every second or so.
*/
void updatePing(); void updatePing();
/** /**Get ping in milliseconds. Will only be valid after a call to updatePing.*/
* Get ping in milliseconds. Will only be valid after a call to updatePing.
*/
int getPing(); int getPing();
/** /**Disconnect from the server.*/
* Disconnect from the server.
*/
void disconnect(); void disconnect();
/**Decompress an input snapshot byte array.*/
byte[] decompressSnapshot(byte[] input, int size);
/** /**
* Discover servers. This should run the callback regardless of whether any servers are found. Should not block. * Discover servers. This should run the callback regardless of whether any servers are found. Should not block.
* Callback should be run on libGDX main thread. * Callback should be run on libGDX main thread.
*/ */
void discover(Consumer<Array<Host>> callback); void discover(Consumer<Array<Host>> callback);
/** /**Ping a host. If an error occured, failed() should be called with the exception.*/
* Ping a host. If an error occured, failed() should be called with the exception.
*/
void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed); void pingHost(String address, int port, Consumer<Host> valid, Consumer<Exception> failed);
/** /**Close all connections.*/
* Close all connections.
*/
void dispose(); void dispose();
} }
/** /**Server implementation.*/
* Server implementation.
*/
public interface ServerProvider{ public interface ServerProvider{
/** /**Host a server at specified port.*/
* Host a server at specified port.
*/
void host(int port) throws IOException; void host(int port) throws IOException;
/** /**Sends a large stream of data to a specific client.*/
* Sends a large stream of data to a specific client.
*/
void sendStream(int id, Streamable stream); void sendStream(int id, Streamable stream);
/** /**Send an object to everyone connected.*/
* Send an object to everyone connected.
*/
void send(Object object, SendMode mode); void send(Object object, SendMode mode);
/** /**Send an object to a specific client ID.*/
* Send an object to a specific client ID.
*/
void sendTo(int id, Object object, SendMode mode); void sendTo(int id, Object object, SendMode mode);
/** /**Send an object to everyone <i>except</i> a client ID.*/
* Send an object to everyone <i>except</i> a client ID.
*/
void sendExcept(int id, Object object, SendMode mode); void sendExcept(int id, Object object, SendMode mode);
/** /**Close the server connection.*/
* Close the server connection.
*/
void close(); void close();
/** /**Compress an input snapshot byte array.*/
* Return all connected users. byte[] compressSnapshot(byte[] input);
*/
/**Return all connected users.*/
Array<? extends NetConnection> getConnections(); Array<? extends NetConnection> getConnections();
/** /**Returns a connection by ID.*/
* Returns a connection by ID.
*/
NetConnection getByID(int id); NetConnection getByID(int id);
/** /**Close all connections.*/
* Close all connections.
*/
void dispose(); void dispose();
} }
} }
@@ -5,7 +5,6 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.consumers.ConsumeItem; import io.anuke.mindustry.world.consumers.ConsumeItem;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
public class Fracker extends SolidPump{ public class Fracker extends SolidPump{
@@ -70,7 +69,7 @@ public class Fracker extends SolidPump{
if(entity.cons.valid() && entity.accumulator < itemUseTime){ if(entity.cons.valid() && entity.accumulator < itemUseTime){
super.update(tile); super.update(tile);
entity.accumulator += Timers.delta(); entity.accumulator += entity.delta();
}else{ }else{
tryDumpLiquid(tile, result); tryDumpLiquid(tile, result);
} }
@@ -64,7 +64,7 @@ public class PowerCrafter extends Block{
GenericCrafterEntity entity = tile.entity(); GenericCrafterEntity entity = tile.entity();
if(entity.cons.valid()){ if(entity.cons.valid()){
entity.progress += 1f / craftTime; entity.progress += 1f / craftTime * entity.delta();
entity.totalProgress += entity.delta(); entity.totalProgress += entity.delta();
} }
@@ -101,7 +101,7 @@ public class PowerSmelter extends PowerBlock{
//heat it up if there's enough power //heat it up if there's enough power
if(entity.cons.valid()){ if(entity.cons.valid()){
entity.heat += 1f / heatUpTime * entity.delta(); entity.heat += 1f / heatUpTime * entity.delta();
if(Mathf.chance(Timers.delta() * burnEffectChance)) if(Mathf.chance(entity.delta() * burnEffectChance))
Effects.effect(burnEffect, entity.x + Mathf.range(size * 4f), entity.y + Mathf.range(size * 4)); Effects.effect(burnEffect, entity.x + Mathf.range(size * 4f), entity.y + Mathf.range(size * 4));
}else{ }else{
entity.heat -= 1f / heatUpTime * Timers.delta(); entity.heat -= 1f / heatUpTime * Timers.delta();
@@ -122,6 +122,8 @@ public class PowerSmelter extends PowerBlock{
} }
} }
entity.craftTime += entity.delta();
if(entity.items.get(result) >= itemCapacity //output full if(entity.items.get(result) >= itemCapacity //output full
|| entity.heat <= minHeat //not burning || entity.heat <= minHeat //not burning
|| entity.craftTime < craftTime*baseSmeltSpeed){ //not yet time || entity.craftTime < craftTime*baseSmeltSpeed){ //not yet time
@@ -80,13 +80,13 @@ public class SolidPump extends Pump{
float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction); float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction);
tile.entity.liquids.add(result, maxPump); tile.entity.liquids.add(result, maxPump);
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f); entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f);
if(Mathf.chance(Timers.delta() * updateEffectChance)) if(Mathf.chance(entity.delta() * updateEffectChance))
Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f)); Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f));
}else{ }else{
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.02f); entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.02f);
} }
entity.pumpTime += entity.warmup * Timers.delta(); entity.pumpTime += entity.warmup * entity.delta();
tryDumpLiquid(tile, result); tryDumpLiquid(tile, result);
} }
@@ -16,6 +16,8 @@ import io.anuke.mindustry.net.Packets.Disconnect;
import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.util.Pooling; import io.anuke.ucore.util.Pooling;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FastDecompressor;
import java.io.IOException; import java.io.IOException;
import java.net.DatagramPacket; import java.net.DatagramPacket;
@@ -32,6 +34,7 @@ public class KryoClient implements ClientProvider{
Client client; Client client;
ObjectMap<InetAddress, Host> addresses = new ObjectMap<>(); ObjectMap<InetAddress, Host> addresses = new ObjectMap<>();
ClientDiscoveryHandler handler; ClientDiscoveryHandler handler;
LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
public KryoClient(){ public KryoClient(){
KryoCore.init(); KryoCore.init();
@@ -105,6 +108,13 @@ public class KryoClient implements ClientProvider{
} }
} }
@Override
public byte[] decompressSnapshot(byte[] input, int size){
byte[] result = new byte[size];
decompressor.decompress(input, result);
return result;
}
@Override @Override
public void connect(String ip, int port) throws IOException { public void connect(String ip, int port) throws IOException {
//just in case //just in case
@@ -20,6 +20,8 @@ import io.anuke.mindustry.net.Packets.StreamChunk;
import io.anuke.ucore.UCore; import io.anuke.ucore.UCore;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;
import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -33,6 +35,7 @@ public class KryoServer implements ServerProvider {
final CopyOnWriteArrayList<KryoConnection> connections = new CopyOnWriteArrayList<>(); final CopyOnWriteArrayList<KryoConnection> connections = new CopyOnWriteArrayList<>();
final CopyOnWriteArraySet<Integer> missing = new CopyOnWriteArraySet<>(); final CopyOnWriteArraySet<Integer> missing = new CopyOnWriteArraySet<>();
final Array<KryoConnection> array = new Array<>(); final Array<KryoConnection> array = new Array<>();
final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
Thread serverThread; Thread serverThread;
int lastconnection = 0; int lastconnection = 0;
@@ -105,6 +108,11 @@ public class KryoServer implements ServerProvider {
} }
} }
@Override
public byte[] compressSnapshot(byte[] input){
return compressor.compress(input);
}
@Override @Override
public Array<KryoConnection> getConnections() { public Array<KryoConnection> getConnections() {
array.clear(); array.clear();