Bulk-serialization optimization

This commit is contained in:
Anuken
2026-07-04 13:01:24 -04:00
parent 3c2004a2e7
commit e58782c838
4 changed files with 97 additions and 21 deletions
+19 -9
View File
@@ -131,6 +131,7 @@ public class NetServer implements ApplicationListener{
private ObjectMap<String, Seq<Cons2<Player, Object>>> logicClientDataHandlers = new ObjectMap<>();
/** Reused Seq<Player> for writing entity snapshots per team. */
private Seq<Player> playersToSend = new Seq<>(false);
private Seq<NetConnection> tempConnections = new Seq<>(false);
/** Used for entity snapshot timing. */
public long snapshotSyncTime;
@@ -1145,6 +1146,13 @@ public class NetServer implements ApplicationListener{
hiddenIds.clear();
int sent = 0;
tempConnections.clear();
for(Player player : players){
//player.con must not be null here (the players seq must ONLY contain non-local connected clients)
tempConnections.add(player.con);
player.con.snapshotsSent ++;
}
for(Syncc entity : Groups.sync){
if(entity.isSyncHidden(team)){
@@ -1158,9 +1166,7 @@ public class NetServer implements ApplicationListener{
if(syncStream.size() > maxSnapshotSize){
dataStream.close();
short ssent = (short)sent;
var bytes = syncStream.toByteArray();
players.each(player -> Call.entitySnapshot(player.con, ssent, bytes));
sendEntitySnapshots(tempConnections, (short)sent, syncStream.toByteArray());
sent = 0;
syncStream.reset();
}
@@ -1168,18 +1174,22 @@ public class NetServer implements ApplicationListener{
if(sent > 0){
dataStream.close();
short ssent = (short)sent;
var bytes = syncStream.toByteArray();
players.each(player -> Call.entitySnapshot(player.con, ssent, bytes));
sendEntitySnapshots(tempConnections, (short)sent, syncStream.toByteArray());
}
if(hiddenIds.size > 0){
players.each(player -> Call.hiddenSnapshot(player.con, hiddenIds));
var packet = new HiddenSnapshotCallPacket();
packet.ids = hiddenIds;
net.send(packet, tempConnections, false);
}
players.each(player -> player.con.snapshotsSent++);
}
protected void sendEntitySnapshots(Seq<NetConnection> connections, short amount, byte[] data){
var packet = new EntitySnapshotCallPacket();
packet.amount = amount;
packet.data = data;
net.send(packet, connections, false);
}
/** Writes a custom snapshot containing player-local entities; this is for entities other players don't see. */
public void writeCustomEntitySnapshot(Player player, Iterable<Syncc> entities) throws IOException{
+46 -3
View File
@@ -36,6 +36,7 @@ public class ArcNetProvider implements NetProvider{
private static final LZ4SafeDecompressor decompressor = LZ4Factory.fastestInstance().safeDecompressor();
private static final LZ4Compressor compressor = LZ4Factory.fastestInstance().fastCompressor();
private static final ThreadLocal<Seq<Connection>> writeConnections = Threads.local(Seq::new);
private volatile int playerLimitCache, packetSpamLimit;
private Ratekeeper clientUdpErrorRate = new Ratekeeper();
@@ -329,6 +330,49 @@ public class ArcNetProvider implements NetProvider{
return connections;
}
@Override
public void sendAllServer(Object object, Iterable<NetConnection> connections, boolean reliable){
//build up list of underlying arcnet connections for faster bulk transfer
var cons = writeConnections.get();
cons.clear();
for(var con : connections){
if(con instanceof ArcConnection ac){
cons.add(ac.connection);
}
}
if(reliable){
server.sendToAllTCP(object, cons);
}else{
server.sendToAllUDP(object, cons);
}
cons.clear();
}
@Override
public void sendAllServer(Object object, boolean reliable){
if(reliable){
server.sendToAllTCP(object);
}else{
server.sendToAllUDP(object);
}
}
@Override
public void sendExceptServer(NetConnection except, Object object, boolean reliable){
if(!(except instanceof ArcConnection con)){
NetProvider.super.sendExceptServer(except, object, reliable);
return;
}
if(reliable){
server.sendToAllExceptTCP(con.connection.getID(), object);
}else{
server.sendToAllExceptUDP(con.connection.getID(), object);
}
}
@Override
public void hostServer(int port) throws IOException{
connections.clear();
@@ -351,7 +395,7 @@ public class ArcNetProvider implements NetProvider{
mainExecutor.submit(server::stop);
}
class ArcConnection extends NetConnection{
public class ArcConnection extends NetConnection{
public final Connection connection;
long lastErrorTime;
@@ -413,8 +457,7 @@ public class ArcNetProvider implements NetProvider{
}
}
}catch(Exception e){
Log.err(e);
Log.info("Error sending packet. Disconnecting invalid client!");
Log.err("Error sending packet. Disconnecting invalid client!", e);
connection.close(DcReason.error);
if(connection.getArbitraryData() instanceof ArcConnection k){
+31 -8
View File
@@ -243,21 +243,20 @@ public class Net{
/** Send an object to all connected clients, or to the server if this is a client.*/
public void send(Object object, boolean reliable){
if(server){
for(NetConnection con : provider.getConnections()){
con.send(object, reliable);
}
provider.sendAllServer(object, reliable);
}else{
provider.sendClient(object, reliable);
}
}
/** Server bulk-send to several clients. */
public void send(Object object, Iterable<NetConnection> connections, boolean reliable){
provider.sendAllServer(object, connections, reliable);
}
/** Send an object to everyone EXCEPT a certain client. Server-side only.*/
public void sendExcept(NetConnection except, Object object, boolean reliable){
for(NetConnection con : getConnections()){
if(con != except){
con.send(object, reliable);
}
}
provider.sendExceptServer(except, object, reliable);
}
public @Nullable StreamBuilder getCurrentStream(){
@@ -413,6 +412,30 @@ public class Net{
/** Networking implementation. */
public interface NetProvider{
/** Sends a packet to a specific list of clients. */
default void sendAllServer(Object object, Iterable<NetConnection> connections, boolean reliable){
for(NetConnection con : connections){
con.send(object, reliable);
}
}
/** Sends a packet to all connected clients. */
default void sendAllServer(Object object, boolean reliable){
for(NetConnection con : getConnections()){
con.send(object, reliable);
}
}
/** Sends a packet to all connected clients, except the specified one. */
default void sendExceptServer(NetConnection except, Object object, boolean reliable){
for(NetConnection con : getConnections()){
if(con != except){
con.send(object, reliable);
}
}
}
/** Connect to a server. */
void connectClient(String ip, int port, Runnable success) throws IOException;
+1 -1
View File
@@ -26,4 +26,4 @@ org.gradle.caching=true
org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000
android.enableR8.fullMode=false
archash=da9887f6e4
archash=f229d1fb3f