mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-03 03:31:28 +03:00
Sync cleanup + player-local entities/labels
This commit is contained in:
@@ -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);
|
||||
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);
|
||||
|
||||
//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;
|
||||
|
||||
@@ -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){
|
||||
|
||||
Reference in New Issue
Block a user