mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-03-13 19:39:04 +07:00
Server-side fog clip + Omnidirectional flare + resprite
This commit is contained in:
parent
5fa4c09b1c
commit
5fa28e6090
Binary file not shown.
Before Width: | Height: | Size: 460 B After Width: | Height: | Size: 417 B |
@ -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();
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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;
|
||||
}};
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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{
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)){
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -49,6 +49,9 @@ public class ScriptConsoleFragment extends Table{
|
||||
if(shown && !open && enableConsole){
|
||||
toggle();
|
||||
}
|
||||
if(shown){
|
||||
chatfield.requestKeyboard();
|
||||
}
|
||||
clearChatInput();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user