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);
atlas = TextureAtlas.blankAtlas();
Vars.net = new Net(platform.getNet());
MapPreviewLoader.setupLoaders();
mods = new Mods();
schematics = new Schematics();

View File

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

View File

@ -958,25 +958,23 @@ public class UnitTypes{
//region air attack
flare = new UnitType("flare"){{
speed = 3f;
speed = 2.7f;
accel = 0.08f;
drag = 0.01f;
drag = 0.04f;
flying = true;
health = 75;
engineOffset = 5.5f;
range = 140f;
health = 70;
engineOffset = 5.75f;
targetAir = false;
//as default AI, flares are not very useful in core rushes, they attack nothing in the way
playerTargetFlags = new BlockFlag[]{null};
targetFlags = new BlockFlag[]{BlockFlag.generator, null};
circleTarget = true;
hitSize = 7;
itemCapacity = 15;
weapons.add(new Weapon(){{
y = 0f;
x = 2f;
reload = 13f;
reload = 20f;
ejectEffect = Fx.casing1;
bullet = new BasicBulletType(2.5f, 9){{
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.updateId ++;
state.teams.updateTeamStats();
MapPreviewLoader.checkPreviews();
if(state.rules.fog){
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)
public static void blockSnapshot(short amount, byte[] data){
try{

View File

@ -35,10 +35,12 @@ import static mindustry.Vars.*;
public class NetServer implements ApplicationListener{
/** 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 FloatBuffer fbuffer = FloatBuffer.allocate(20);
private static final Writes dataWrites = new Writes(null);
private static final IntSeq hiddenIds = new IntSeq();
private static final Vec2 vector = new Vec2();
/** If a player goes away of their server-side coordinates by this distance, they get teleported back. */
private static final float correctDist = tilesize * 14f;
@ -935,9 +937,16 @@ public class NetServer implements ApplicationListener{
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()); //write type ID
@ -959,6 +968,10 @@ public class NetServer implements ApplicationListener{
Call.entitySnapshot(player.con, (short)sent, syncStream.toByteArray());
}
if(hiddenIds.size > 0){
Call.hiddenSnapshot(player.con, hiddenIds);
}
player.con.snapshotsSent ++;
}
@ -1013,6 +1026,7 @@ public class NetServer implements ApplicationListener{
void sync(){
try{
int interval = Config.snapshotInterval.num();
Groups.player.each(p -> !p.isLocal(), player -> {
if(player.con == null || !player.con.isConnected()){
onDisconnect(player, "disappeared");
@ -1021,7 +1035,7 @@ public class NetServer implements ApplicationListener{
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();

View File

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

View File

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

View File

@ -134,6 +134,19 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
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
public float range(){
return type.maxRange;

View File

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

View File

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

View File

@ -105,7 +105,7 @@ public class BlockRenderer{
});
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){
event.tile.build.wasVisible = visible;
}

View File

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

View File

@ -585,6 +585,23 @@ public class TypeIO{
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){
write.b(cont.getContentType().ordinal());
write.s(cont.id);

View File

@ -9,6 +9,9 @@ import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.ctype.*;
import mindustry.game.EventType.*;
import java.lang.reflect.*;
public class MapPreviewLoader extends TextureLoader{
@ -54,4 +57,37 @@ public class MapPreviewLoader extends TextureLoader{
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),
autosaveAmount("The maximum amount of autosaves. Older ones get replaced.", 10),
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();

View File

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

View File

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

View File

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

View File

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