Sync cleanup + player-local entities/labels

This commit is contained in:
Anuken
2026-07-04 11:18:07 -04:00
parent c756faddac
commit e3a877c948
6 changed files with 85 additions and 96 deletions
+56 -83
View File
@@ -114,11 +114,6 @@ public class NetServer implements ApplicationListener{
public static float voteDuration = 0.5f * 60;
/** Cooldown between votes in seconds. */
public static int voteCooldown = 60 * 5;
/**
* If this is true, isSyncHidden will called only once per team if FoW is enabled, or not at all.
* Set this to false if a mod uses isSyncHidden to hide entities.
*/
public boolean skipHiddenEntitiesCheck = true;
private ReusableByteOutStream writeBuffer = new ReusableByteOutStream(127);
private Writes outputBuffer = new Writes(new DataOutputStream(writeBuffer));
@@ -1116,48 +1111,6 @@ public class NetServer implements ApplicationListener{
universe.seconds(), tps, GlobalVars.rand.seed0, GlobalVars.rand.seed1, syncStream.toByteArray());
}
public void writeEntitySnapshot(Player player) throws IOException{
syncStream.reset();
hiddenIds.clear();
int sent = 0;
for(Syncc entity : Groups.sync){
//TODO write to special list
if(entity.isSyncHidden(player)){
hiddenIds.add(entity.id());
continue;
}
//write all entities now
dataStream.writeInt(entity.id()); //write id
dataStream.writeByte(entity.classId() & 0xFF); //write type ID
entity.beforeWrite();
entity.writeSync(dataStreamWrites); //write entity itself
sent++;
if(syncStream.size() > maxSnapshotSize){
dataStream.close();
Call.entitySnapshot(player.con, (short)sent, syncStream.toByteArray());
sent = 0;
syncStream.reset();
}
}
if(sent > 0){
dataStream.close();
Call.entitySnapshot(player.con, (short)sent, syncStream.toByteArray());
}
if(hiddenIds.size > 0){
Call.hiddenSnapshot(player.con, hiddenIds);
}
player.con.snapshotsSent++;
}
/** Does not check isSyncHidden. Call this if no entities are hidden. */
public void writeEntitySnapshotsAll() throws IOException{
syncStream.reset();
@@ -1165,11 +1118,7 @@ public class NetServer implements ApplicationListener{
int sent = 0;
for(Syncc entity : Groups.sync){
//write all entities now
dataStream.writeInt(entity.id()); //write id
dataStream.writeByte(entity.classId() & 0xFF); //write type ID
entity.beforeWrite();
entity.writeSync(dataStreamWrites); //write entity itself
writeEntity(entity, dataStream);
sent++;
@@ -1191,30 +1140,25 @@ public class NetServer implements ApplicationListener{
}
/** Checks isSyncHidden for only one player per team. Called if FoW is enabled but there is no custom syncHidden. */
public void writeEntitySnapshotsTeam(Seq<Player> players) throws IOException{
public void writeEntitySnapshotsTeam(Team team, Seq<Player> players) throws IOException{
syncStream.reset();
hiddenIds.clear();
int sent = 0;
for(Syncc entity : Groups.sync){
//TODO write to special list
if(entity.isSyncHidden(players.first())){
if(entity.isSyncHidden(team)){
hiddenIds.add(entity.id());
continue;
}
//write all entities now
dataStream.writeInt(entity.id()); //write id
dataStream.writeByte(entity.classId() & 0xFF); //write type ID
entity.beforeWrite();
entity.writeSync(dataStreamWrites); //write entity itself
writeEntity(entity, dataStream);
sent++;
if(syncStream.size() > maxSnapshotSize){
dataStream.close();
final var ssent = (short)sent;
short ssent = (short)sent;
var bytes = syncStream.toByteArray();
players.each(player -> Call.entitySnapshot(player.con, ssent, bytes));
sent = 0;
@@ -1224,7 +1168,7 @@ public class NetServer implements ApplicationListener{
if(sent > 0){
dataStream.close();
final var ssent = (short)sent;
short ssent = (short)sent;
var bytes = syncStream.toByteArray();
players.each(player -> Call.entitySnapshot(player.con, ssent, bytes));
}
@@ -1236,6 +1180,40 @@ public class NetServer implements ApplicationListener{
players.each(player -> player.con.snapshotsSent++);
}
/** 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{
syncStream.reset();
int sent = 0;
for(Syncc entity : entities){
writeEntity(entity, dataStream);
sent++;
if(syncStream.size() > maxSnapshotSize){
dataStream.close();
Call.entitySnapshot(player.con, (short)sent, syncStream.toByteArray());
sent = 0;
syncStream.reset();
}
}
if(sent > 0){
dataStream.close();
Call.entitySnapshot(player.con, (short)sent, syncStream.toByteArray());
}
}
protected void writeEntity(Syncc entity, DataOutputStream dataStream) throws IOException{
dataStream.writeInt(entity.id()); //write id
dataStream.writeByte(entity.classId() & 0xFF); //write type ID
entity.beforeWrite();
entity.writeSync(dataStreamWrites); //write entity itself
}
public String fixName(String name){
name = name.trim().replace("\n", "").replace("\t", "");
if(name.equals("[") || name.equals("]")){
@@ -1291,7 +1269,6 @@ public class NetServer implements ApplicationListener{
Groups.player.each(p -> !p.isLocal(), player -> {
if(player.con == null || !player.con.isConnected()){
onDisconnect(player, "disappeared");
return;
}
});
@@ -1300,29 +1277,25 @@ public class NetServer implements ApplicationListener{
writeStateSnapshot();
if(skipHiddenEntitiesCheck){
if(Vars.state.rules.fog){
//Serialize by teams
for(Team team : Team.all){ //Not Teams.active, because players can be on inactive teams
var tdata = team.data();
playersToSend.selectFrom(tdata.players, p -> !p.isLocal() && p.con.hasConnected);
if(!playersToSend.isEmpty()){
writeEntitySnapshotsTeam(playersToSend);
}
if(Vars.state.rules.fog){
//Serialize by teams
for(Team team : Team.all){ //Not Teams.active, because players can be on inactive teams
var tdata = team.data();
playersToSend.selectFrom(tdata.players, p -> !p.isLocal() && p.con.hasConnected);
if(!playersToSend.isEmpty()){
writeEntitySnapshotsTeam(team, playersToSend);
}
}else{
//Serialize once for all players
writeEntitySnapshotsAll();
}
}else{
//Serialize for each player
Groups.player.each(p -> !p.isLocal() && p.con.hasConnected, player -> {
try{
writeEntitySnapshot(player);
}catch(IOException e){
Log.err(e);
}
});
//Serialize once for all players
writeEntitySnapshotsAll();
}
//write custom player-specific entities (usually labels)
for(Player player : Groups.player){
if(player.con != null && player.con.hasConnected && player.con.localEntities.size > 0){
writeCustomEntitySnapshot(player, player.con.localEntities);
}
}
}
@@ -3,6 +3,7 @@ package mindustry.entities.comp;
import arc.util.io.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.game.*;
import mindustry.gen.*;
import java.nio.*;
@@ -21,7 +22,7 @@ abstract class SyncComp implements Entityc{
void afterSync(){}
void interpolate(){}
boolean isSyncHidden(Player player){
boolean isSyncHidden(Team team){
return false;
}
@@ -213,9 +213,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
@Override
@Replace
public boolean isSyncHidden(Player player){
public boolean isSyncHidden(Team team){
//shooting reveals position so bullets can be seen
return !isShooting() && inFogTo(player.team());
return !isShooting() && inFogTo(team);
}
@Override
@@ -17,14 +17,14 @@ import mindustry.ui.*;
public abstract class WorldLabelComp implements Posc, Drawc, Syncc{
@Import int id;
@Import float x, y;
@Import boolean added;
public static final byte
flagBackground = 1,
flagOutline = 2,
flagAlignLeft = 4,
flagAlignRight = 8,
flagAutoscale = 16,
flagOnlyParentVisible = 32;
flagBackground = 1 << 0,
flagOutline = 1 << 1,
flagAlignLeft = 1 << 2,
flagAlignRight = 1 << 3,
flagAutoscale = 1 << 4;
public String text = "sample text";
public float fontSize = 1f, z = Layer.playerName + 1;
@@ -37,6 +37,7 @@ public abstract class WorldLabelComp implements Posc, Drawc, Syncc{
@Replace
public float clipSize(){
if(parent != null) return Float.MAX_VALUE;
return text.length() * 10f * fontSize;
}
@@ -54,9 +55,6 @@ public abstract class WorldLabelComp implements Posc, Drawc, Syncc{
if(parent != null){
x += parent.x();
y += parent.y();
if((flags & flagOnlyParentVisible) != 0 && !parent.isLocal()){
return;
}
}
drawAt(text, x, y, z, flags, fontSize, Align.center, (flags & flagAlignLeft) != 0 ? Align.left : (flags & flagAlignRight) != 0 ? Align.right : Align.center);
}
@@ -111,6 +109,21 @@ public abstract class WorldLabelComp implements Posc, Drawc, Syncc{
Draw.z(z);
}
/** Makes this label visible only to the specific player. This must be called instead of add(). */
public void show(Player player){
if(added || player.con == null) return;
player.con.localEntities.add(this);
added = true;
}
/** Hides this player-specific label. If you used {@link #show(Player)} previously, you must call this method instead of {@link #hide()}! */
public void hide(Player player){
if(!added || player.con == null) return;
player.con.localEntities.remove(this);
Call.removeWorldLabel(player.con, id);
added = false;
}
/** This MUST be called instead of remove()! */
public void hide(){
remove();
@@ -31,6 +31,8 @@ public abstract class NetConnection{
public Ratekeeper chatRate = new Ratekeeper();
/** Handles packet spam rate limits. */
public Ratekeeper packetRate = new Ratekeeper();
/** Entities that only this player will get synced to them. */
public Seq<Syncc> localEntities = new Seq<>(false);
//TODO: refactor to state enum
public boolean hasConnected, hasBegunConnecting, determiningAssets, receivingAssets, hasDisconnected;
+1 -1
View File
@@ -200,7 +200,7 @@ public class Menus{
}
//internal use only
@Remote
@Remote(variants = Variant.both)
public static void removeWorldLabel(int id){
var label = Groups.label.getByID(id);
if(label != null){