Partial 7.0 merge - API preview

This commit is contained in:
Anuken
2021-06-02 11:08:08 -04:00
parent ea75a357ca
commit 28b235ef07
531 changed files with 12356 additions and 6286 deletions

View File

@@ -369,7 +369,7 @@ public class Administration{
ObjectSet<PlayerInfo> result = new ObjectSet<>();
for(PlayerInfo info : playerInfo.values()){
if(info.lastName.equalsIgnoreCase(name) || (info.names.contains(name, false))
if(info.lastName.equalsIgnoreCase(name) || info.names.contains(name, false)
|| Strings.stripColors(Strings.stripColors(info.lastName)).equals(name)
|| info.ips.contains(name, false) || info.id.equals(name)){
result.add(info);
@@ -617,6 +617,9 @@ public class Administration{
/** valid for unit-type events only, and even in that case may be null. */
public @Nullable Unit unit;
/** valid only for removePlanned events only; contains packed positions. */
public @Nullable int[] plans;
public PlayerAction set(Player player, ActionType type, Tile tile){
this.player = player;
this.type = type;
@@ -641,11 +644,12 @@ public class Administration{
tile = null;
block = null;
unit = null;
plans = null;
}
}
public enum ActionType{
breakBlock, placeBlock, rotate, configure, withdrawItem, depositItem, control, command
breakBlock, placeBlock, rotate, configure, withdrawItem, depositItem, control, buildSelect, command, removePlanned
}
}

View File

@@ -2,14 +2,17 @@ package mindustry.net;
import arc.*;
import arc.func.*;
import arc.math.*;
import arc.net.*;
import arc.net.FrameworkMessage.*;
import arc.struct.*;
import arc.util.*;
import arc.util.Log.*;
import arc.util.async.*;
import arc.util.pooling.*;
import arc.util.io.*;
import mindustry.net.Net.*;
import mindustry.net.Packets.*;
import net.jpountz.lz4.*;
import java.io.*;
import java.net.*;
@@ -28,8 +31,15 @@ public class ArcNetProvider implements NetProvider{
final CopyOnWriteArrayList<ArcConnection> connections = new CopyOnWriteArrayList<>();
Thread serverThread;
private static final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
private static final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
public ArcNetProvider(){
ArcNet.errorHandler = e -> Log.debug(Strings.getStackTrace(e));
ArcNet.errorHandler = e -> {
if(Log.level == LogLevel.debug){
Log.debug(Strings.getStackTrace(e));
}
};
client = new Client(8192, 8192, new PacketSerializer());
client.setDiscoveryPacket(packetSupplier);
@@ -56,11 +66,11 @@ public class ArcNetProvider implements NetProvider{
@Override
public void received(Connection connection, Object object){
if(object instanceof FrameworkMessage) return;
if(!(object instanceof Packet p)) return;
Core.app.post(() -> {
try{
net.handleClientReceived(object);
net.handleClientReceived(p);
}catch(Throwable e){
net.handleException(e);
}
@@ -111,13 +121,13 @@ public class ArcNetProvider implements NetProvider{
@Override
public void received(Connection connection, Object object){
ArcConnection k = getByArcID(connection.getID());
if(object instanceof FrameworkMessage || k == null) return;
if(!(object instanceof Packet pack) || k == null) return;
Core.app.post(() -> {
try{
net.handleServerReceived(k, object);
net.handleServerReceived(k, pack);
}catch(Throwable e){
e.printStackTrace();
Log.err(e);
}
});
}
@@ -163,9 +173,9 @@ public class ArcNetProvider implements NetProvider{
}
@Override
public void sendClient(Object object, SendMode mode){
public void sendClient(Object object, boolean reliable){
try{
if(mode == SendMode.tcp){
if(reliable){
client.sendTCP(object);
}else{
client.sendUDP(object);
@@ -174,8 +184,6 @@ public class ArcNetProvider implements NetProvider{
}catch(BufferOverflowException | BufferUnderflowException e){
net.showError(e);
}
Pools.free(object);
}
@Override
@@ -293,7 +301,7 @@ public class ArcNetProvider implements NetProvider{
//send an object so the receiving side knows how to handle the following chunks
StreamBegin begin = new StreamBegin();
begin.total = stream.stream.available();
begin.type = Registrator.getID(stream.getClass());
begin.type = Net.getPacketId(stream);
connection.sendTCP(begin);
id = begin.id;
}
@@ -309,9 +317,9 @@ public class ArcNetProvider implements NetProvider{
}
@Override
public void send(Object object, SendMode mode){
public void send(Object object, boolean reliable){
try{
if(mode == SendMode.tcp){
if(reliable){
connection.sendTCP(object);
}else{
connection.sendUDP(object);
@@ -332,32 +340,129 @@ public class ArcNetProvider implements NetProvider{
}
}
@SuppressWarnings("unchecked")
public static class PacketSerializer implements NetSerializer{
//for debugging total read/write speeds
private static final boolean debug = false;
ThreadLocal<ByteBuffer> decompressBuffer = new ThreadLocal<>(){
@Override
protected ByteBuffer initialValue(){
return ByteBuffer.allocate(32768);
}
};
ThreadLocal<Reads> reads = new ThreadLocal<>(){
@Override
protected Reads initialValue(){
return new Reads(new ByteBufferInput(decompressBuffer.get()));
}
};
ThreadLocal<Writes> writes = new ThreadLocal<>(){
@Override
protected Writes initialValue(){
return new Writes(new ByteBufferOutput(decompressBuffer.get()));
}
};
//for debugging network write counts
static WindowedMean upload = new WindowedMean(5), download = new WindowedMean(5);
static long lastUpload, lastDownload, uploadAccum, downloadAccum;
static int lastPos;
@Override
public Object read(ByteBuffer byteBuffer){
if(debug){
if(Time.timeSinceMillis(lastDownload) >= 1000){
lastDownload = Time.millis();
download.add(downloadAccum);
downloadAccum = 0;
Log.info("Download: @ b/s", download.mean());
}
downloadAccum += byteBuffer.remaining();
}
byte id = byteBuffer.get();
if(id == -2){
return readFramework(byteBuffer);
}else{
Packet packet = Pools.obtain((Class<Packet>)Registrator.getByID(id).type, (Prov<Packet>)Registrator.getByID(id).constructor);
packet.read(byteBuffer);
//read length int, followed by compressed lz4 data
//TODO not thread safe!!!
Packet packet = Net.newPacket(id);
var buffer = decompressBuffer.get();
int length = byteBuffer.getShort() & 0xffff;
byte compression = byteBuffer.get();
//no compression, copy over buffer
if(compression == 0){
buffer.position(0).limit(length);
buffer.put(byteBuffer.array(), byteBuffer.position(), length);
buffer.position(0);
packet.read(reads.get());
//move read packets forward
byteBuffer.position(byteBuffer.position() + buffer.position());
}else{
//decompress otherwise
int read = decompressor.decompress(byteBuffer, byteBuffer.position(), buffer, 0, length);
buffer.position(0);
buffer.limit(length);
packet.read(reads.get());
//move buffer forward based on bytes read by decompressor
byteBuffer.position(byteBuffer.position() + read);
}
return packet;
}
}
@Override
public void write(ByteBuffer byteBuffer, Object o){
if(o instanceof FrameworkMessage){
if(debug){
lastPos = byteBuffer.position();
}
//write raw buffer
if(o instanceof ByteBuffer raw){
byteBuffer.put(raw);
}else if(o instanceof FrameworkMessage msg){
byteBuffer.put((byte)-2); //code for framework message
writeFramework(byteBuffer, (FrameworkMessage)o);
writeFramework(byteBuffer, msg);
}else{
if(!(o instanceof Packet)) throw new RuntimeException("All sent objects must implement be Packets! Class: " + o.getClass());
byte id = Registrator.getID(o.getClass());
if(id == -1) throw new RuntimeException("Unregistered class: " + o.getClass());
if(!(o instanceof Packet pack)) throw new RuntimeException("All sent objects must implement be Packets! Class: " + o.getClass());
byte id = Net.getPacketId(pack);
byteBuffer.put(id);
((Packet)o).write(byteBuffer);
var temp = decompressBuffer.get();
temp.position(0);
temp.limit(temp.capacity());
pack.write(writes.get());
short length = (short)temp.position();
//write length, uncompressed
byteBuffer.putShort(length);
//don't bother with small packets
if(length < 36 || pack instanceof StreamChunk){
//write direct contents...
byteBuffer.put((byte)0); //0 = no compression
byteBuffer.put(temp.array(), 0, length);
}else{
byteBuffer.put((byte)1); //1 = compression
//write compressed data; this does not modify position!
int written = compressor.compress(temp, 0, temp.position(), byteBuffer, byteBuffer.position(), byteBuffer.remaining());
//skip to indicate the written, compressed data
byteBuffer.position(byteBuffer.position() + written);
}
}
if(debug){
if(Time.timeSinceMillis(lastUpload) >= 1000){
lastUpload = Time.millis();
upload.add(uploadAccum);
uploadAccum = 0;
Log.info("Upload: @ b/s", upload.mean());
}
uploadAccum += byteBuffer.position() - lastPos;
}
}
@@ -388,9 +493,9 @@ public class ArcNetProvider implements NetProvider{
p.isReply = buffer.get() == 1;
return p;
}else if(id == 1){
return new DiscoverHost();
return FrameworkMessage.discoverHost;
}else if(id == 2){
return new KeepAlive();
return FrameworkMessage.keepAlive;
}else if(id == 3){
RegisterUDP p = new RegisterUDP();
p.connectionID = buffer.getInt();

View File

@@ -5,33 +5,62 @@ import arc.func.*;
import arc.net.*;
import arc.struct.*;
import arc.util.*;
import arc.util.pooling.*;
import mindustry.gen.*;
import mindustry.net.Packets.*;
import mindustry.net.Streamable.*;
import net.jpountz.lz4.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import static arc.util.Log.*;
import static mindustry.Vars.*;
@SuppressWarnings("unchecked")
public class Net{
private static Seq<Prov<? extends Packet>> packetProvs = new Seq<>();
private static Seq<Class<? extends Packet>> packetClasses = new Seq<>();
private static ObjectIntMap<Class<?>> packetToId = new ObjectIntMap<>();
private boolean server;
private boolean active;
private boolean clientLoaded;
private @Nullable StreamBuilder currentStream;
private final Seq<Object> packetQueue = new Seq<>();
private final Seq<Packet> packetQueue = new Seq<>();
private final ObjectMap<Class<?>, Cons> clientListeners = new ObjectMap<>();
private final ObjectMap<Class<?>, Cons2<NetConnection, Object>> serverListeners = new ObjectMap<>();
private final IntMap<StreamBuilder> streams = new IntMap<>();
private final NetProvider provider;
private final LZ4FastDecompressor decompressor = LZ4Factory.fastestInstance().fastDecompressor();
private final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
static{
registerPacket(StreamBegin::new);
registerPacket(StreamChunk::new);
registerPacket(WorldStream::new);
registerPacket(ConnectPacket::new);
//register generated packet classes
Call.registerPackets();
}
/** Registers a new packet type for serialization. */
public static <T extends Packet> void registerPacket(Prov<T> cons){
packetProvs.add(cons);
var t = cons.get();
packetClasses.add(t.getClass());
packetToId.put(t.getClass(), packetProvs.size - 1);
}
public static byte getPacketId(Packet packet){
int id = packetToId.get(packet.getClass(), -1);
if(id == -1) throw new ArcRuntimeException("Unknown packet type: " + packet.getClass());
return (byte)id;
}
public static <T extends Packet> T newPacket(byte id){
return ((Prov<T>)packetProvs.get(id & 0xff)).get();
}
public Net(NetProvider provider){
this.provider = provider;
@@ -39,9 +68,9 @@ public class Net{
public void handleException(Throwable e){
if(e instanceof ArcNetException){
Core.app.post(() -> showError(new IOException("mismatch")));
Core.app.post(() -> showError(new IOException("mismatch", e)));
}else if(e instanceof ClosedChannelException){
Core.app.post(() -> showError(new IOException("alreadyconnected")));
Core.app.post(() -> showError(new IOException("alreadyconnected", e)));
}else{
Core.app.post(() -> showError(e));
}
@@ -171,14 +200,6 @@ public class Net{
active = false;
}
public byte[] compressSnapshot(byte[] input){
return compressor.compress(input);
}
public byte[] decompressSnapshot(byte[] input, int size){
return decompressor.decompress(input, size);
}
/**
* Starts discovering servers on a different thread.
* Callback is run on the main Arc thread.
@@ -195,21 +216,21 @@ public class Net{
}
/** Send an object to all connected clients, or to the server if this is a client.*/
public void send(Object object, SendMode mode){
public void send(Object object, boolean reliable){
if(server){
for(NetConnection con : provider.getConnections()){
con.send(object, mode);
con.send(object, reliable);
}
}else{
provider.sendClient(object, mode);
provider.sendClient(object, reliable);
}
}
/** Send an object to everyone EXCEPT a certain client. Server-side only.*/
public void sendExcept(NetConnection except, Object object, SendMode mode){
public void sendExcept(NetConnection except, Object object, boolean reliable){
for(NetConnection con : getConnections()){
if(con != except){
con.send(object, mode);
con.send(object, reliable);
}
}
}
@@ -235,7 +256,7 @@ public class Net{
/**
* Call to handle a packet being received for the client.
*/
public void handleClientReceived(Object object){
public void handleClientReceived(Packet object){
if(object instanceof StreamBegin b){
streams.put(b.id, currentStream = new StreamBuilder(b));
@@ -251,33 +272,43 @@ public class Net{
handleClientReceived(builder.build());
currentStream = null;
}
}else if(clientListeners.get(object.getClass()) != null){
}else{
int p = object.getPriority();
if(clientLoaded || ((object instanceof Packet) && ((Packet)object).isImportant())){
if(clientLoaded || p == Packet.priorityHigh){
if(clientListeners.get(object.getClass()) != null){
clientListeners.get(object.getClass()).get(object);
}else{
object.handleClient();
}
Pools.free(object);
}else if(!((object instanceof Packet) && ((Packet)object).isUnimportant())){
}else if(p != Packet.priorityLow){
packetQueue.add(object);
}else{
Pools.free(object);
}
}else{
Log.err("Unhandled packet type: '@'!", object);
}
}
/**
* Call to handle a packet being received for the server.
*/
public void handleServerReceived(NetConnection connection, Object object){
if(serverListeners.get(object.getClass()) != null){
serverListeners.get(object.getClass()).get(connection, object);
Pools.free(object);
}else{
Log.err("Unhandled packet type: '@'!", object.getClass());
public void handleServerReceived(NetConnection connection, Packet object){
try{
//handle object normally
if(serverListeners.get(object.getClass()) != null){
serverListeners.get(object.getClass()).get(connection, object);
}else{
object.handleServer(connection);
}
}catch(ValidateException e){
//ignore invalid actions
debug("Validation failed for '@': @", e.player, e.getMessage());
}catch(RuntimeException e){
//ignore indirect ValidateException-s
if(e.getCause() instanceof ValidateException v){
debug("Validation failed for '@': @", v.player, v.getMessage());
}else{
//rethrow if not ValidateException
throw e;
}
}
}
@@ -315,17 +346,13 @@ public class Net{
active = false;
}
public enum SendMode{
tcp, udp
}
/** Networking implementation. */
public interface NetProvider{
/** Connect to a server. */
void connectClient(String ip, int port, Runnable success) throws IOException;
/** Send an object to the server. */
void sendClient(Object object, SendMode mode);
void sendClient(Object object, boolean reliable);
/** Disconnect from the server. */
void disconnectClient();
@@ -355,4 +382,5 @@ public class Net{
closeServer();
}
}
}

View File

@@ -5,7 +5,6 @@ import arc.util.*;
import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.net.Administration.*;
import mindustry.net.Net.*;
import mindustry.net.Packets.*;
import java.io.*;
@@ -40,7 +39,7 @@ public abstract class NetConnection{
public void kick(KickReason reason){
if(kicked) return;
Log.info("Kicking connection @; Reason: @", address, reason.name());
Log.info("Kicking connection @ / @; Reason: @", address, uuid, reason.name());
if((reason == KickReason.kick || reason == KickReason.banned || reason == KickReason.vote)){
PlayerInfo info = netServer.admins.getInfo(uuid);
@@ -65,7 +64,7 @@ public abstract class NetConnection{
public void kick(String reason, long kickDuration){
if(kicked) return;
Log.info("Kicking connection @; Reason: @", address, reason.replace("\n", " "));
Log.info("Kicking connection @ / @; Reason: @", address, uuid, reason.replace("\n", " "));
netServer.admins.handleKicked(uuid, address, kickDuration);
@@ -86,8 +85,8 @@ public abstract class NetConnection{
int cid;
StreamBegin begin = new StreamBegin();
begin.total = stream.stream.available();
begin.type = Registrator.getID(stream.getClass());
send(begin, SendMode.tcp);
begin.type = Net.getPacketId(stream);
send(begin, true);
cid = begin.id;
while(stream.stream.available() > 0){
@@ -97,14 +96,14 @@ public abstract class NetConnection{
StreamChunk chunk = new StreamChunk();
chunk.id = cid;
chunk.data = bytes;
send(chunk, SendMode.tcp);
send(chunk, true);
}
}catch(IOException e){
throw new RuntimeException(e);
}
}
public abstract void send(Object object, SendMode mode);
public abstract void send(Object object, boolean reliable);
public abstract void close();
}

View File

@@ -46,6 +46,7 @@ public class NetworkIO{
SaveIO.getSaveWriter().writeContentHeader(stream);
SaveIO.getSaveWriter().writeMap(stream);
SaveIO.getSaveWriter().writeTeamBlocks(stream);
}catch(IOException e){
throw new RuntimeException(e);
}
@@ -70,6 +71,7 @@ public class NetworkIO{
SaveIO.getSaveWriter().readContentHeader(stream);
SaveIO.getSaveWriter().readMap(stream, world.context);
SaveIO.getSaveWriter().readTeamBlocks(stream);
}catch(IOException e){
throw new RuntimeException(e);
}finally{

View File

@@ -1,19 +1,24 @@
package mindustry.net;
import arc.util.pooling.Pool.*;
import arc.util.io.*;
import java.nio.*;
public abstract class Packet{
//these are constants because I don't want to bother making an enum to mirror the annotation enum
public interface Packet extends Poolable{
default void read(ByteBuffer buffer){}
default void write(ByteBuffer buffer){}
default void reset(){}
/** Does not get handled unless client is connected. */
public static final int priorityLow = 0;
/** Gets put in a queue and processed if not connected. */
public static final int priorityNormal = 1;
/** Gets handled immediately, regardless of connection status. */
public static final int priorityHigh = 2;
default boolean isImportant(){
return false;
public void read(Reads read){}
public void write(Writes write){}
public int getPriority(){
return priorityNormal;
}
default boolean isUnimportant(){
return false;
}
public void handleClient(){}
public void handleServer(NetConnection con){}
}

View File

@@ -2,13 +2,12 @@ package mindustry.net;
import arc.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import arc.util.serialization.*;
import mindustry.core.*;
import mindustry.io.*;
import java.io.*;
import java.nio.*;
import java.util.zip.*;
/**
@@ -45,21 +44,23 @@ public class Packets{
kick, ban, trace, wave
}
public static class Connect implements Packet{
/** Generic client connection event. */
public static class Connect extends Packet{
public String addressTCP;
@Override
public boolean isImportant(){
return true;
public int getPriority(){
return priorityHigh;
}
}
public static class Disconnect implements Packet{
/** Generic client disconnection event. */
public static class Disconnect extends Packet{
public String reason;
@Override
public boolean isImportant(){
return true;
public int getPriority(){
return priorityHigh;
}
}
@@ -67,55 +68,8 @@ public class Packets{
}
public static class InvokePacket implements Packet{
private static ReusableByteInStream bin;
private static Reads read = new Reads(new DataInputStream(bin = new ReusableByteInStream()));
public byte type, priority;
public byte[] bytes;
public int length;
@Override
public void read(ByteBuffer buffer){
type = buffer.get();
priority = buffer.get();
short writeLength = buffer.getShort();
bytes = new byte[writeLength];
buffer.get(bytes);
}
@Override
public void write(ByteBuffer buffer){
buffer.put(type);
buffer.put(priority);
buffer.putShort((short)length);
buffer.put(bytes, 0, length);
}
@Override
public void reset(){
priority = 0;
}
@Override
public boolean isImportant(){
return priority == 1;
}
@Override
public boolean isUnimportant(){
return priority == 2;
}
public Reads reader(){
bin.setBytes(bytes);
return read;
}
}
/** Marks the beginning of a stream. */
public static class StreamBegin implements Packet{
public static class StreamBegin extends Packet{
private static int lastid;
public int id = lastid++;
@@ -123,40 +77,39 @@ public class Packets{
public byte type;
@Override
public void write(ByteBuffer buffer){
buffer.putInt(id);
buffer.putInt(total);
buffer.put(type);
public void write(Writes buffer){
buffer.i(id);
buffer.i(total);
buffer.b(type);
}
@Override
public void read(ByteBuffer buffer){
id = buffer.getInt();
total = buffer.getInt();
type = buffer.get();
public void read(Reads buffer){
id = buffer.i();
total = buffer.i();
type = buffer.b();
}
}
public static class StreamChunk implements Packet{
public static class StreamChunk extends Packet{
public int id;
public byte[] data;
@Override
public void write(ByteBuffer buffer){
buffer.putInt(id);
buffer.putShort((short)data.length);
buffer.put(data);
public void write(Writes buffer){
buffer.i(id);
buffer.s((short)data.length);
buffer.b(data);
}
@Override
public void read(ByteBuffer buffer){
id = buffer.getInt();
data = new byte[buffer.getShort()];
buffer.get(data);
public void read(Reads buffer){
id = buffer.i();
data = buffer.b(buffer.s());
}
}
public static class ConnectPacket implements Packet{
public static class ConnectPacket extends Packet{
public int version;
public String versionType;
public Seq<String> mods;
@@ -165,40 +118,41 @@ public class Packets{
public int color;
@Override
public void write(ByteBuffer buffer){
buffer.putInt(Version.build);
public void write(Writes buffer){
buffer.i(Version.build);
TypeIO.writeString(buffer, versionType);
TypeIO.writeString(buffer, name);
TypeIO.writeString(buffer, locale);
TypeIO.writeString(buffer, usid);
byte[] b = Base64Coder.decode(uuid);
buffer.put(b);
buffer.b(b);
CRC32 crc = new CRC32();
crc.update(Base64Coder.decode(uuid), 0, b.length);
buffer.putLong(crc.getValue());
buffer.l(crc.getValue());
buffer.put(mobile ? (byte)1 : 0);
buffer.putInt(color);
buffer.put((byte)mods.size);
Log.info("CRC value sent: @", Long.toHexString(crc.getValue()));
buffer.b(mobile ? (byte)1 : 0);
buffer.i(color);
buffer.b((byte)mods.size);
for(int i = 0; i < mods.size; i++){
TypeIO.writeString(buffer, mods.get(i));
}
}
@Override
public void read(ByteBuffer buffer){
version = buffer.getInt();
public void read(Reads buffer){
version = buffer.i();
versionType = TypeIO.readString(buffer);
name = TypeIO.readString(buffer);
locale = TypeIO.readString(buffer);
usid = TypeIO.readString(buffer);
byte[] idbytes = new byte[16];
buffer.get(idbytes);
byte[] idbytes = buffer.b(16);
uuid = new String(Base64Coder.encode(idbytes));
mobile = buffer.get() == 1;
color = buffer.getInt();
int totalMods = buffer.get();
mobile = buffer.b() == 1;
color = buffer.i();
int totalMods = buffer.b();
mods = new Seq<>(totalMods);
for(int i = 0; i < totalMods; i++){
mods.add(TypeIO.readString(buffer));

View File

@@ -1,45 +0,0 @@
package mindustry.net;
import arc.func.*;
import arc.struct.*;
import mindustry.net.Packets.*;
public class Registrator{
private static final ClassEntry[] classes = {
new ClassEntry(StreamBegin.class, StreamBegin::new),
new ClassEntry(StreamChunk.class, StreamChunk::new),
new ClassEntry(WorldStream.class, WorldStream::new),
new ClassEntry(ConnectPacket.class, ConnectPacket::new),
new ClassEntry(InvokePacket.class, InvokePacket::new)
};
private static final ObjectIntMap<Class<?>> ids = new ObjectIntMap<>();
static{
if(classes.length > 127) throw new RuntimeException("Can't have more than 127 registered classes!");
for(int i = 0; i < classes.length; i++){
ids.put(classes[i].type, i);
}
}
public static ClassEntry getByID(byte id){
return classes[id];
}
public static byte getID(Class<?> type){
return (byte)ids.get(type, -1);
}
public static ClassEntry[] getClasses(){
return classes;
}
public static class ClassEntry{
public final Class<?> type;
public final Prov<?> constructor;
public <T extends Packet> ClassEntry(Class<T> type, Prov<T> constructor){
this.type = type;
this.constructor = constructor;
}
}
}

View File

@@ -4,12 +4,12 @@ import mindustry.net.Packets.*;
import java.io.*;
public class Streamable implements Packet{
public class Streamable extends Packet{
public transient ByteArrayInputStream stream;
@Override
public boolean isImportant(){
return true;
public int getPriority(){
return priorityHigh;
}
public static class StreamBuilder{
@@ -37,7 +37,7 @@ public class Streamable implements Packet{
}
public Streamable build(){
Streamable s = (Streamable)Registrator.getByID(type).constructor.get();
Streamable s = Net.newPacket(type);
s.stream = new ByteArrayInputStream(stream.toByteArray());
return s;
}