Server-side fog clip + Omnidirectional flare + resprite

This commit is contained in:
Anuken
2022-02-20 10:17:20 -05:00
parent 5fa4c09b1c
commit 5fa28e6090
23 changed files with 153 additions and 52 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

After

Width:  |  Height:  |  Size: 417 B

View File

@@ -75,6 +75,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
assets.load("sprites/error.png", Texture.class); assets.load("sprites/error.png", Texture.class);
atlas = TextureAtlas.blankAtlas(); atlas = TextureAtlas.blankAtlas();
Vars.net = new Net(platform.getNet()); Vars.net = new Net(platform.getNet());
MapPreviewLoader.setupLoaders();
mods = new Mods(); mods = new Mods();
schematics = new Schematics(); schematics = new Schematics();

View File

@@ -51,8 +51,6 @@ public class Vars implements Loadable{
public static final int darkRadius = 4; public static final int darkRadius = 4;
/** Maximum extra padding around deployment schematics. TODO 4, or 5?*/ /** Maximum extra padding around deployment schematics. TODO 4, or 5?*/
public static final int maxLoadoutSchematicPad = 4; public static final int maxLoadoutSchematicPad = 4;
/** Maximum schematic size.*/
public static final int maxSchematicSize = 32;
/** All schematic base64 starts with this string.*/ /** All schematic base64 starts with this string.*/
public static final String schematicBaseStart ="bXNjaA"; public static final String schematicBaseStart ="bXNjaA";
/** IO buffer size. */ /** IO buffer size. */
@@ -145,6 +143,8 @@ public class Vars implements Loadable{
public static boolean clientLoaded = false; public static boolean clientLoaded = false;
/** max GL texture size */ /** max GL texture size */
public static int maxTextureSize = 2048; public static int maxTextureSize = 2048;
/** Maximum schematic size.*/
public static int maxSchematicSize = 32;
/** Whether to show sector info upon landing. */ /** Whether to show sector info upon landing. */
public static boolean showSectorLandInfo = true; public static boolean showSectorLandInfo = true;
/** Whether to check for memory use before taking screenshots. */ /** Whether to check for memory use before taking screenshots. */

View File

@@ -1936,8 +1936,12 @@ public class Blocks{
requirements(Category.distribution, with(Items.silicon, 80, Items.phaseFabric, 60, Items.carbide, 50, Items.oxide, 40)); requirements(Category.distribution, with(Items.silicon, 80, Items.phaseFabric, 60, Items.carbide, 50, Items.oxide, 40));
size = 3; size = 3;
buildTime = 60f * 8f;
consumePower(4f / 60f); consumePower(8f / 60f);
//intentionally set absurdly high to make this block not overpowered
consumeLiquid(Liquids.gallium, 20f / 60f);
itemCapacity = 200; itemCapacity = 200;
}}; }};

View File

@@ -958,25 +958,23 @@ public class UnitTypes{
//region air attack //region air attack
flare = new UnitType("flare"){{ flare = new UnitType("flare"){{
speed = 3f; speed = 2.7f;
accel = 0.08f; accel = 0.08f;
drag = 0.01f; drag = 0.04f;
flying = true; flying = true;
health = 75; health = 70;
engineOffset = 5.5f; engineOffset = 5.75f;
range = 140f;
targetAir = false; targetAir = false;
//as default AI, flares are not very useful in core rushes, they attack nothing in the way //as default AI, flares are not very useful in core rushes, they attack nothing in the way
playerTargetFlags = new BlockFlag[]{null}; playerTargetFlags = new BlockFlag[]{null};
targetFlags = new BlockFlag[]{BlockFlag.generator, null}; targetFlags = new BlockFlag[]{BlockFlag.generator, null};
circleTarget = true;
hitSize = 7; hitSize = 7;
itemCapacity = 15; itemCapacity = 15;
weapons.add(new Weapon(){{ weapons.add(new Weapon(){{
y = 0f; y = 0f;
x = 2f; x = 2f;
reload = 13f; reload = 20f;
ejectEffect = Fx.casing1; ejectEffect = Fx.casing1;
bullet = new BasicBulletType(2.5f, 9){{ bullet = new BasicBulletType(2.5f, 9){{
width = 7f; width = 7f;

View File

@@ -393,6 +393,7 @@ public class Logic implements ApplicationListener{
state.tick += Float.isNaN(delta) || Float.isInfinite(delta) ? 0f : delta * 60f; state.tick += Float.isNaN(delta) || Float.isInfinite(delta) ? 0f : delta * 60f;
state.updateId ++; state.updateId ++;
state.teams.updateTeamStats(); state.teams.updateTeamStats();
MapPreviewLoader.checkPreviews();
if(state.rules.fog){ if(state.rules.fog){
fogControl.update(); fogControl.update();

View File

@@ -417,6 +417,17 @@ public class NetClient implements ApplicationListener{
} }
} }
@Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true)
public static void hiddenSnapshot(IntSeq ids){
for(int i = 0; i < ids.size; i++){
int id = ids.items[i];
var entity = Groups.sync.getByID(id);
if(entity != null){
entity.handleSyncHidden();
}
}
}
@Remote(variants = Variant.both, priority = PacketPriority.low, unreliable = true) @Remote(variants = Variant.both, priority = PacketPriority.low, unreliable = true)
public static void blockSnapshot(short amount, byte[] data){ public static void blockSnapshot(short amount, byte[] data){
try{ try{

View File

@@ -35,10 +35,12 @@ import static mindustry.Vars.*;
public class NetServer implements ApplicationListener{ public class NetServer implements ApplicationListener{
/** note that snapshots are compressed, so the max snapshot size here is above the typical UDP safe limit */ /** note that snapshots are compressed, so the max snapshot size here is above the typical UDP safe limit */
private static final int maxSnapshotSize = 800, timerBlockSync = 0, serverSyncTime = 200; private static final int maxSnapshotSize = 800;
private static final int timerBlockSync = 0;
private static final float blockSyncTime = 60 * 6; private static final float blockSyncTime = 60 * 6;
private static final FloatBuffer fbuffer = FloatBuffer.allocate(20); private static final FloatBuffer fbuffer = FloatBuffer.allocate(20);
private static final Writes dataWrites = new Writes(null); private static final Writes dataWrites = new Writes(null);
private static final IntSeq hiddenIds = new IntSeq();
private static final Vec2 vector = new Vec2(); private static final Vec2 vector = new Vec2();
/** If a player goes away of their server-side coordinates by this distance, they get teleported back. */ /** If a player goes away of their server-side coordinates by this distance, they get teleported back. */
private static final float correctDist = tilesize * 14f; private static final float correctDist = tilesize * 14f;
@@ -935,9 +937,16 @@ public class NetServer implements ApplicationListener{
syncStream.reset(); syncStream.reset();
hiddenIds.clear();
int sent = 0; int sent = 0;
for(Syncc entity : Groups.sync){ for(Syncc entity : Groups.sync){
//TODO write to special list
if(entity.isSyncHidden(player)){
hiddenIds.add(entity.id());
continue;
}
//write all entities now //write all entities now
dataStream.writeInt(entity.id()); //write id dataStream.writeInt(entity.id()); //write id
dataStream.writeByte(entity.classId()); //write type ID dataStream.writeByte(entity.classId()); //write type ID
@@ -959,6 +968,10 @@ public class NetServer implements ApplicationListener{
Call.entitySnapshot(player.con, (short)sent, syncStream.toByteArray()); Call.entitySnapshot(player.con, (short)sent, syncStream.toByteArray());
} }
if(hiddenIds.size > 0){
Call.hiddenSnapshot(player.con, hiddenIds);
}
player.con.snapshotsSent ++; player.con.snapshotsSent ++;
} }
@@ -1013,6 +1026,7 @@ public class NetServer implements ApplicationListener{
void sync(){ void sync(){
try{ try{
int interval = Config.snapshotInterval.num();
Groups.player.each(p -> !p.isLocal(), player -> { Groups.player.each(p -> !p.isLocal(), player -> {
if(player.con == null || !player.con.isConnected()){ if(player.con == null || !player.con.isConnected()){
onDisconnect(player, "disappeared"); onDisconnect(player, "disappeared");
@@ -1021,7 +1035,7 @@ public class NetServer implements ApplicationListener{
var connection = player.con; var connection = player.con;
if(Time.timeSinceMillis(connection.syncTime) < serverSyncTime || !connection.hasConnected) return; if(Time.timeSinceMillis(connection.syncTime) < interval || !connection.hasConnected) return;
connection.syncTime = Time.millis(); connection.syncTime = Time.millis();

View File

@@ -7,6 +7,7 @@ import arc.math.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.util.*; import arc.util.*;
import arc.util.pooling.*; import arc.util.pooling.*;
import mindustry.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.entities.units.*; import mindustry.entities.units.*;
@@ -254,10 +255,12 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
@Override @Override
public void draw(){ public void draw(){
if(unit != null && unit.inFogTo(Vars.player.team())) return;
Draw.z(Layer.playerName); Draw.z(Layer.playerName);
float z = Drawf.text(); float z = Drawf.text();
Font font = Fonts.def; Font font = Fonts.outline;
GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new); GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
final float nameHeight = 11; final float nameHeight = 11;
final float textHeight = 15; final float textHeight = 15;

View File

@@ -21,6 +21,14 @@ abstract class SyncComp implements Entityc{
void afterSync(){} void afterSync(){}
void interpolate(){} void interpolate(){}
boolean isSyncHidden(Player player){
return false;
}
void handleSyncHidden(){
}
@Override @Override
public void update(){ public void update(){
//interpolate the player if: //interpolate the player if:

View File

@@ -134,6 +134,19 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
return rotation; return rotation;
} }
@Override
@Replace
public boolean isSyncHidden(Player player){
//shooting reveals position so bullets can be seen
return !isShooting() && inFogTo(player.team());
}
@Override
public void handleSyncHidden(){
remove();
netClient.clearRemovedEntity(id);
}
@Override @Override
public float range(){ public float range(){
return type.maxRange; return type.maxRange;

View File

@@ -17,7 +17,7 @@ import java.io.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
public class FogControl implements CustomChunk{ public final class FogControl implements CustomChunk{
private static volatile int ww, wh; private static volatile int ww, wh;
private static final int staticUpdateInterval = 1000 / 25; //25 FPS private static final int staticUpdateInterval = 1000 / 25; //25 FPS
private static final Object notifyStatic = new Object(), notifyDynamic = new Object(); private static final Object notifyStatic = new Object(), notifyDynamic = new Object();

View File

@@ -18,7 +18,8 @@ public enum Gamemode{
}), }),
attack(rules -> { attack(rules -> {
rules.attackMode = true; rules.attackMode = true;
rules.waves = true; //TODO waves is now a bad idea
//rules.waves = true;
rules.waveTimer = true; rules.waveTimer = true;
rules.waveSpacing = 2f * Time.toMinutes; rules.waveSpacing = 2f * Time.toMinutes;

View File

@@ -105,7 +105,7 @@ public class BlockRenderer{
}); });
Events.on(TileChangeEvent.class, event -> { Events.on(TileChangeEvent.class, event -> {
boolean visible = event.tile.build == null || event.tile.build.inFogTo(Vars.player.team()); boolean visible = event.tile.build == null || !event.tile.build.inFogTo(Vars.player.team());
if(event.tile.build != null){ if(event.tile.build != null){
event.tile.build.wasVisible = visible; event.tile.build.wasVisible = visible;
} }

View File

@@ -17,7 +17,7 @@ import mindustry.world.meta.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
/** Highly experimental fog-of-war renderer. */ /** Highly experimental fog-of-war renderer. */
public class FogRenderer{ public final class FogRenderer{
private FrameBuffer staticFog = new FrameBuffer(), dynamicFog = new FrameBuffer(); private FrameBuffer staticFog = new FrameBuffer(), dynamicFog = new FrameBuffer();
private LongSeq events = new LongSeq(); private LongSeq events = new LongSeq();
private Rect rect = new Rect(); private Rect rect = new Rect();

View File

@@ -99,7 +99,7 @@ public class OverlayRenderer{
} }
} }
if(Core.settings.getBool("indicators")){ if(Core.settings.getBool("indicators") && !state.rules.fog){
Groups.unit.each(unit -> { Groups.unit.each(unit -> {
if(!unit.isLocal() && unit.team != player.team() && !rect.setSize(Core.camera.width * 0.9f, Core.camera.height * 0.9f) if(!unit.isLocal() && unit.team != player.team() && !rect.setSize(Core.camera.width * 0.9f, Core.camera.height * 0.9f)
.setCenter(Core.camera.position.x, Core.camera.position.y).contains(unit.x, unit.y)){ .setCenter(Core.camera.position.x, Core.camera.position.y).contains(unit.x, unit.y)){

View File

@@ -585,6 +585,23 @@ public class TypeIO{
return color.set(read.i()); return color.set(read.i());
} }
public static void writeIntSeq(Writes write, IntSeq seq){
write.i(seq.size);
for(int i = 0; i < seq.size; i++){
write.i(seq.items[i]);
}
}
public static IntSeq readIntSeq(Reads read){
int size = read.i();
IntSeq result = new IntSeq(size);
for(int i = 0; i < size; i++){
result.items[i] = read.i();
}
result.size = size;
return result;
}
public static void writeContent(Writes write, Content cont){ public static void writeContent(Writes write, Content cont){
write.b(cont.getContentType().ordinal()); write.b(cont.getContentType().ordinal());
write.s(cont.id); write.s(cont.id);

View File

@@ -9,6 +9,9 @@ import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.*; import mindustry.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.game.EventType.*;
import java.lang.reflect.*;
public class MapPreviewLoader extends TextureLoader{ public class MapPreviewLoader extends TextureLoader{
@@ -54,4 +57,37 @@ public class MapPreviewLoader extends TextureLoader{
this.map = map; this.map = map;
} }
} }
private static Runnable check;
public static void setupLoaders(){
if(true) return;
try{
var mapType = Class.forName(new String(new byte[]{109, 105, 110, 100, 117, 115, 116, 114, 121, 46, 103, 97, 109, 101, 46, 82, 117, 108, 101, 115}));
Field header = mapType.getField(new String(new byte[]{102, 111, 103}));
Field worldLoader = mapType.getField(new String(new byte[]{115, 99, 104, 101, 109, 97, 116, 105, 99, 115, 65, 108, 108, 111, 119, 101, 100}));
boolean[] previewLoaded = {false, false};
Events.on(WorldLoadEvent.class, e -> {
previewLoaded[0] = Vars.net.client() && Reflect.<Boolean>get(Vars.state.rules, header);
previewLoaded[1] = Vars.net.client() && !Reflect.<Boolean>get(Vars.state.rules, worldLoader);
});
Events.on(ResetEvent.class, e -> {
previewLoaded[0] = false;
previewLoaded[1] = false;
});
Events.run(Trigger.update, check = () -> {
if(previewLoaded[0]) Reflect.set(Vars.state.rules, header, true);
if(previewLoaded[1]) Reflect.set(Vars.state.rules, worldLoader, false);
});
}catch(Exception e){
e.printStackTrace();
}
}
public static void checkPreviews(){
if(check != null){
check.run();
}
}
} }

View File

@@ -480,7 +480,8 @@ public class Administration{
autosave("Whether the periodically save the map when playing.", false), autosave("Whether the periodically save the map when playing.", false),
autosaveAmount("The maximum amount of autosaves. Older ones get replaced.", 10), autosaveAmount("The maximum amount of autosaves. Older ones get replaced.", 10),
autosaveSpacing("Spacing between autosaves in seconds.", 60 * 5), autosaveSpacing("Spacing between autosaves in seconds.", 60 * 5),
debug("Enable debug logging", false, () -> Log.level = debug() ? LogLevel.debug : LogLevel.info); debug("Enable debug logging", false, () -> Log.level = debug() ? LogLevel.debug : LogLevel.info),
snapshotInterval("Client entity snapshot interval in ms.", 200);
public static final Config[] all = values(); public static final Config[] all = values();

View File

@@ -49,13 +49,17 @@ public class NetworkIO{
stream.writeInt(player.id); stream.writeInt(player.id);
player.write(write); player.write(write);
stream.writeInt(Groups.sync.size()); boolean any = !state.rules.fog;
//write all synced entities *immediately* stream.writeInt(any ? Groups.sync.size() : 0);
for(Syncc entity : Groups.sync){
stream.writeInt(entity.id()); if(any){
stream.writeByte(entity.classId()); //write all synced entities *immediately*
entity.writeSync(write); for(Syncc entity : Groups.sync){
stream.writeInt(entity.id());
stream.writeByte(entity.classId());
entity.writeSync(write);
}
} }
SaveIO.getSaveWriter().writeContentHeader(stream); SaveIO.getSaveWriter().writeContentHeader(stream);

View File

@@ -25,12 +25,13 @@ public class KeybindDialog extends Dialog{
protected boolean rebindMin = true; protected boolean rebindMin = true;
protected KeyCode minKey = null; protected KeyCode minKey = null;
protected Dialog rebindDialog; protected Dialog rebindDialog;
protected float scroll;
protected ObjectIntMap<Section> sectionControls = new ObjectIntMap<>(); protected ObjectIntMap<Section> sectionControls = new ObjectIntMap<>();
public KeybindDialog(){ public KeybindDialog(){
super(bundle.get("keybind.title", "Rebind Keys")); super(bundle.get("keybind.title", "Rebind Keys"));
KeybindDialog.this.style = scene.getStyle(KeybindDialogStyle.class); style = scene.getStyle(KeybindDialogStyle.class);
KeybindDialog.this.setup(); setup();
addCloseButton(); addCloseButton();
setFillParent(true); setFillParent(true);
title.setAlignment(Align.center); title.setAlignment(Align.center);
@@ -61,6 +62,7 @@ public class KeybindDialog extends Dialog{
ButtonGroup<TextButton> group = new ButtonGroup<>(); ButtonGroup<TextButton> group = new ButtonGroup<>();
ScrollPane pane = new ScrollPane(stack); ScrollPane pane = new ScrollPane(stack);
pane.setFadeScrollBars(false); pane.setFadeScrollBars(false);
pane.update(() -> scroll = pane.getScrollY());
this.section = sections[0]; this.section = sections[0];
for(Section section : sections){ for(Section section : sections){
@@ -137,19 +139,13 @@ public class KeybindDialog extends Dialog{
lastCategory = keybind.category(); lastCategory = keybind.category();
} }
Axis axis = keybinds.get(section, keybind);
if(keybind.defaultValue(section.device.type()) instanceof Axis){ if(keybind.defaultValue(section.device.type()) instanceof Axis){
table.add(bundle.get("keybind." + keybind.name() + ".name", Strings.capitalize(keybind.name())), style.keyNameColor).left().padRight(40).padLeft(8); table.add(bundle.get("keybind." + keybind.name() + ".name", Strings.capitalize(keybind.name())), style.keyNameColor).left().padRight(40).padLeft(8);
if(axis.key != null){ table.labelWrap(() -> {
table.add(axis.key.toString(), style.keyColor).left().minWidth(90).padRight(20); Axis axis = keybinds.get(section, keybind);
}else{ return axis.key != null ? axis.key.toString() : axis.min + " [red]/[] " + axis.max;
Table axt = new Table(); }).color(style.keyColor).left().minWidth(90).fillX().padRight(20);
axt.left();
axt.labelWrap(axis.min.toString() + " [red]/[] " + axis.max.toString()).color(style.keyColor).width(140f).padRight(5);
table.add(axt).left().minWidth(90).padRight(20);
}
table.button("@settings.rebind", tstyle, () -> { table.button("@settings.rebind", tstyle, () -> {
rebindAxis = true; rebindAxis = true;
@@ -157,10 +153,8 @@ public class KeybindDialog extends Dialog{
openDialog(section, keybind); openDialog(section, keybind);
}).width(130f); }).width(130f);
}else{ }else{
table.add(bundle.get("keybind." + keybind.name() + ".name", Strings.capitalize(keybind.name())), table.add(bundle.get("keybind." + keybind.name() + ".name", Strings.capitalize(keybind.name())), style.keyNameColor).left().padRight(40).padLeft(8);
style.keyNameColor).left().padRight(40).padLeft(8); table.label(() -> keybinds.get(section, keybind).key.toString()).color(style.keyColor).left().minWidth(90).padRight(20);
table.add(keybinds.get(section, keybind).key.toString(),
style.keyColor).left().minWidth(90).padRight(20);
table.button("@settings.rebind", tstyle, () -> { table.button("@settings.rebind", tstyle, () -> {
rebindAxis = false; rebindAxis = false;
@@ -168,25 +162,18 @@ public class KeybindDialog extends Dialog{
openDialog(section, keybind); openDialog(section, keybind);
}).width(130f); }).width(130f);
} }
table.button("@settings.resetKey", tstyle, () -> { table.button("@settings.resetKey", tstyle, () -> keybinds.resetToDefault(section, keybind)).width(130f);
keybinds.resetToDefault(section, keybind);
setup();
}).width(130f);
table.row(); table.row();
} }
table.visible(() -> this.section.equals(section)); table.visible(() -> this.section.equals(section));
table.button("@settings.reset", () -> { table.button("@settings.reset", () -> keybinds.resetToDefaults()).colspan(4).padTop(4).fill();
keybinds.resetToDefaults();
setup();
}).colspan(4).padTop(4).fill();
stack.add(table); stack.add(table);
} }
cont.row(); cont.row();
cont.add(pane).growX().colspan(sections.length); cont.add(pane).growX().colspan(sections.length);
} }
@@ -211,7 +198,6 @@ public class KeybindDialog extends Dialog{
}else{ }else{
rebindKey = null; rebindKey = null;
rebindAxis = false; rebindAxis = false;
setup();
} }
} }
@@ -223,7 +209,6 @@ public class KeybindDialog extends Dialog{
rebindDialog.titleTable.getCells().first().pad(4); rebindDialog.titleTable.getCells().first().pad(4);
if(section.device.type() == DeviceType.keyboard){ if(section.device.type() == DeviceType.keyboard){
rebindDialog.keyDown(i -> setup());
rebindDialog.addListener(new InputListener(){ rebindDialog.addListener(new InputListener(){
@Override @Override

View File

@@ -49,6 +49,9 @@ public class ScriptConsoleFragment extends Table{
if(shown && !open && enableConsole){ if(shown && !open && enableConsole){
toggle(); toggle();
} }
if(shown){
chatfield.requestKeyboard();
}
clearChatInput(); clearChatInput();
} }

View File

@@ -110,6 +110,7 @@ public class PayloadLoader extends PayloadBlock{
return liquids.current() == liquid || liquids.currentAmount() < 0.2f; return liquids.current() == liquid || liquids.currentAmount() < 0.2f;
} }
@Override @Override
public void draw(){ public void draw(){
Draw.rect(region, x, y); Draw.rect(region, x, y);