Merge branch 'master' into crater
This commit is contained in:
@@ -21,6 +21,7 @@ import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Net;
|
||||
import mindustry.world.blocks.defense.ForceProjector.*;
|
||||
|
||||
@@ -53,8 +54,10 @@ public class Vars implements Loadable{
|
||||
public static final String crashReportURL = "http://192.99.169.18/report";
|
||||
/** URL the links to the wiki's modding guide.*/
|
||||
public static final String modGuideURL = "https://mindustrygame.github.io/wiki/modding/";
|
||||
/** URL to the JSON file containing all the global, public servers. */
|
||||
/** URL to the JSON file containing all the global, public servers. Not queried in BE. */
|
||||
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers.json";
|
||||
/** URL to the JSON file containing all the BE servers. Only queried in BE. */
|
||||
public static final String serverJsonBeURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
|
||||
/** URL the links to the wiki's modding guide.*/
|
||||
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?template=bug_report.md";
|
||||
/** list of built-in servers.*/
|
||||
@@ -136,6 +139,8 @@ public class Vars implements Loadable{
|
||||
public static Fi modDirectory;
|
||||
/** data subdirectory used for schematics */
|
||||
public static Fi schematicDirectory;
|
||||
/** data subdirectory used for bleeding edge build versions */
|
||||
public static Fi bebuildDirectory;
|
||||
/** map file extension */
|
||||
public static final String mapExtension = "msav";
|
||||
/** save file extension */
|
||||
@@ -157,6 +162,7 @@ public class Vars implements Loadable{
|
||||
public static Platform platform = new Platform(){};
|
||||
public static Mods mods;
|
||||
public static Schematics schematics = new Schematics();
|
||||
public static BeControl becontrol;
|
||||
|
||||
public static World world;
|
||||
public static Maps maps;
|
||||
@@ -220,6 +226,7 @@ public class Vars implements Loadable{
|
||||
defaultWaves = new DefaultWaves();
|
||||
collisions = new EntityCollisions();
|
||||
world = new World();
|
||||
becontrol = new BeControl();
|
||||
|
||||
maps = new Maps();
|
||||
spawner = new WaveSpawner();
|
||||
@@ -260,6 +267,7 @@ public class Vars implements Loadable{
|
||||
tmpDirectory = dataDirectory.child("tmp/");
|
||||
modDirectory = dataDirectory.child("mods/");
|
||||
schematicDirectory = dataDirectory.child("schematics/");
|
||||
bebuildDirectory = dataDirectory.child("be_builds/");
|
||||
|
||||
modDirectory.mkdirs();
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ public class Pathfinder implements Runnable{
|
||||
//stop looping when interrupted externally
|
||||
return;
|
||||
}
|
||||
}catch(Exception e){
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,7 +1240,7 @@ public class Blocks implements ContentList{
|
||||
//region storage
|
||||
|
||||
coreShard = new CoreBlock("core-shard"){{
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
|
||||
alwaysUnlocked = true;
|
||||
|
||||
health = 1100;
|
||||
@@ -1249,7 +1249,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
coreFoundation = new CoreBlock("core-foundation"){{
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 400, Items.silicon, 3000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
|
||||
|
||||
health = 2000;
|
||||
itemCapacity = 9000;
|
||||
@@ -1257,7 +1257,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
coreNucleus = new CoreBlock("core-nucleus"){{
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with(Items.titanium, 4000, Items.silicon, 2000, Items.surgealloy, 3000));
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, ItemStack.with());
|
||||
|
||||
health = 4000;
|
||||
itemCapacity = 13000;
|
||||
|
||||
@@ -3,8 +3,6 @@ package mindustry.content;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class Loadouts implements ContentList{
|
||||
public static Schematic
|
||||
basicShard,
|
||||
@@ -14,13 +12,9 @@ public class Loadouts implements ContentList{
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
try{
|
||||
basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA==");
|
||||
advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ=");
|
||||
basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA==");
|
||||
basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1");
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
basicShard = Schematics.readBase64("bXNjaAB4nD2K2wqAIBiD5ymibnoRn6YnEP1BwUMoBL19FuJ2sbFvUFgYZDaJsLeQrkinN9UJHImsNzlYE7WrIUastuSbnlKx2VJJt+8IQGGKdfO/8J5yrGJSMegLg+YUIA==");
|
||||
advancedShard = Schematics.readBase64("bXNjaAB4nD2LjQqAIAyET7OMIOhFfJqeYMxBgSkYCL199gu33fFtB4tOwUTaBCP5QpHFzwtl32DahBeKK1NwPq8hoOcUixwpY+CUxe3XIwBbB/pa6tadVCUP02hgHvp5vZq/0b7pBHPYFOQ=");
|
||||
basicFoundation = Schematics.readBase64("bXNjaAB4nD1OSQ6DMBBzFhVu8BG+0X8MQyoiJTNSukj8nlCi2Adbtg/GA4OBF8oB00rvyE/9ykafqOIw58A7SWRKy1ZiShhZ5RcOLZhYS1hefQ1gRIeptH9jq/qW2lvc1d2tgWsOfVX/tOwE86AYBA==");
|
||||
basicNucleus = Schematics.readBase64("bXNjaAB4nD2MUQqAIBBEJy0s6qOLdJXuYNtCgikYBd2+LNmdj308hkGHtkId7M4YFns4mk/yfB4a48602eDI+mlNznu0FMPFd0wYKCaewl8F0EOueqM+yKSLVfJrNKWnSw/FZGzEGXFG9sy/px4gEBW1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +209,7 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Events.fire(Trigger.update);
|
||||
|
||||
if(!state.is(State.menu)){
|
||||
if(!net.client()){
|
||||
@@ -260,7 +261,7 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
if(!net.client() && !world.isInvalidMap() && !state.isEditor()){
|
||||
if(!net.client() && !world.isInvalidMap() && !state.isEditor() && !state.rules.canGameOver){
|
||||
checkGameOver();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import arc.struct.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.CommandHandler.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -26,9 +26,11 @@ import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.*;
|
||||
import java.util.zip.*;
|
||||
|
||||
import static arc.util.Log.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class NetServer implements ApplicationListener{
|
||||
@@ -217,7 +219,6 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
//playing in pvp mode automatically assigns players to teams
|
||||
player.setTeam(assignTeam(player, playerGroup.all()));
|
||||
Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam());
|
||||
|
||||
sendWorldData(player);
|
||||
|
||||
@@ -599,6 +600,7 @@ public class NetServer implements ApplicationListener{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
if(!headless && !closing && net.server() && state.is(State.menu)){
|
||||
@@ -616,6 +618,20 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
/** Should only be used on the headless backend. */
|
||||
public void openServer(){
|
||||
try{
|
||||
net.host(Config.port.num());
|
||||
info("&lcOpened a server on port {0}.", Config.port.num());
|
||||
}catch(BindException e){
|
||||
Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network.");
|
||||
state.set(State.menu);
|
||||
}catch(IOException e){
|
||||
err(e);
|
||||
state.set(State.menu);
|
||||
}
|
||||
}
|
||||
|
||||
public void kickAll(KickReason reason){
|
||||
for(NetConnection con : net.getConnections()){
|
||||
con.kick(reason);
|
||||
|
||||
@@ -125,7 +125,7 @@ public class Renderer implements ApplicationListener{
|
||||
TileEntity core = player.getClosestCore();
|
||||
if(core != null && player.spawner == null){
|
||||
camera.position.lerpDelta(core.x, core.y, 0.08f);
|
||||
}else{
|
||||
}else if(core != null){
|
||||
camera.position.lerpDelta(position, 0.08f);
|
||||
}
|
||||
}else if(control.input instanceof DesktopInput){
|
||||
|
||||
@@ -9,9 +9,9 @@ import arc.util.io.*;
|
||||
|
||||
public class Version{
|
||||
/** Build type. 'official' for official releases; 'custom' or 'bleeding edge' are also used. */
|
||||
public static String type;
|
||||
public static String type = "unknown";
|
||||
/** Build modifier, e.g. 'alpha' or 'release' */
|
||||
public static String modifier;
|
||||
public static String modifier = "unknown";
|
||||
/** Number specifying the major version, e.g. '4' */
|
||||
public static int number;
|
||||
/** Build number, e.g. '43'. set to '-1' for custom builds. */
|
||||
|
||||
@@ -217,7 +217,7 @@ public class World{
|
||||
public void loadMap(Map map, Rules checkRules){
|
||||
try{
|
||||
SaveIO.load(map.file, new FilterContext(map));
|
||||
}catch(Exception e){
|
||||
}catch(Throwable e){
|
||||
Log.err(e);
|
||||
if(!headless){
|
||||
ui.showErrorMessage("$map.invalid");
|
||||
|
||||
@@ -7,11 +7,13 @@ import arc.graphics.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.entities.traits.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.collisions;
|
||||
|
||||
/** Represents a group of a certain type of entity.*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class EntityGroup<T extends Entity>{
|
||||
public class EntityGroup<T extends Entity> implements Iterable<T>{
|
||||
private final boolean useTree;
|
||||
private final int id;
|
||||
private final Class<T> type;
|
||||
@@ -253,8 +255,13 @@ public class EntityGroup<T extends Entity>{
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns the logic-only array for iteration. */
|
||||
/** Returns the array for iteration. */
|
||||
public Array<T> all(){
|
||||
return entityArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator(){
|
||||
return entityArray.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ public class EventType{
|
||||
exclusionDeath,
|
||||
suicideBomb,
|
||||
openWiki,
|
||||
teamCoreDamage
|
||||
teamCoreDamage,
|
||||
socketConfigChanged,
|
||||
update
|
||||
}
|
||||
|
||||
public static class WinEvent{}
|
||||
|
||||
@@ -70,6 +70,8 @@ public class Rules{
|
||||
public boolean editor = false;
|
||||
/** Whether the tutorial is enabled. False by default. */
|
||||
public boolean tutorial = false;
|
||||
/** Whether a gameover can happen at all. Set this to false to implement custom gameover conditions. */
|
||||
public boolean canGameOver = true;
|
||||
/** Starting items put in cores */
|
||||
public Array<ItemStack> loadout = Array.with(ItemStack.with(Items.copper, 100));
|
||||
/** Blocks that cannot be placed. */
|
||||
|
||||
@@ -249,6 +249,7 @@ public class Schematics implements Loadable{
|
||||
|
||||
public void placeLoadout(Schematic schem, int x, int y){
|
||||
Stile coreTile = schem.tiles.find(s -> s.block instanceof CoreBlock);
|
||||
if(coreTile == null) throw new IllegalArgumentException("Schematic has no core tile. Exiting.");
|
||||
int ox = x - coreTile.x, oy = y - coreTile.y;
|
||||
schem.tiles.each(st -> {
|
||||
Tile tile = world.tile(st.x + ox, st.y + oy);
|
||||
@@ -339,7 +340,8 @@ public class Schematics implements Loadable{
|
||||
for(int cy = oy; cy <= oy2; cy++){
|
||||
Tile tile = world.ltile(cx, cy);
|
||||
|
||||
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock) && tile.entity.block.isVisible()){
|
||||
if(tile != null && tile.entity != null && !counted.contains(tile.pos()) && !(tile.block() instanceof BuildBlock)
|
||||
&& (tile.entity.block.isVisible() || (tile.entity.block instanceof CoreBlock && Core.settings.getBool("coreselect")))){
|
||||
int config = tile.entity.config();
|
||||
if(tile.block().posConfig){
|
||||
config = Pos.get(Pos.x(config) + offsetX, Pos.y(config) + offsetY);
|
||||
@@ -368,8 +370,12 @@ public class Schematics implements Loadable{
|
||||
//region IO methods
|
||||
|
||||
/** Loads a schematic from base64. May throw an exception. */
|
||||
public static Schematic readBase64(String schematic) throws IOException{
|
||||
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
|
||||
public static Schematic readBase64(String schematic){
|
||||
try{
|
||||
return read(new ByteArrayInputStream(Base64Coder.decode(schematic)));
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Schematic read(Fi file) throws IOException{
|
||||
|
||||
@@ -82,6 +82,7 @@ public class MinimapRenderer implements Disposable{
|
||||
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
|
||||
|
||||
for(Unit unit : units){
|
||||
if(unit.isDead()) continue;
|
||||
float rx = (unit.x - rect.x) / rect.width * w;
|
||||
float ry = (unit.y - rect.y) / rect.width * h;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.entities.traits.BuilderTrait.*;
|
||||
@@ -135,6 +136,12 @@ public class DesktopInput extends InputHandler{
|
||||
ui.listfrag.toggle();
|
||||
}
|
||||
|
||||
if(player.getClosestCore() == null){
|
||||
//move camera around
|
||||
float camSpeed = 6f;
|
||||
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta() * camSpeed));
|
||||
}
|
||||
|
||||
if(Core.input.keyRelease(Binding.select)){
|
||||
player.isShooting = false;
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -18,11 +18,6 @@ public class Administration{
|
||||
private Array<ChatFilter> chatFilters = new Array<>();
|
||||
|
||||
public Administration(){
|
||||
Core.settings.defaults(
|
||||
"strict", true,
|
||||
"servername", "Server"
|
||||
);
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -51,21 +46,12 @@ public class Administration{
|
||||
Core.settings.putSave("playerlimit", limit);
|
||||
}
|
||||
|
||||
public void setStrict(boolean on){
|
||||
Core.settings.putSave("strict", on);
|
||||
}
|
||||
|
||||
public boolean getStrict(){
|
||||
return Core.settings.getBool("strict");
|
||||
return Config.strict.bool();
|
||||
}
|
||||
|
||||
public boolean allowsCustomClients(){
|
||||
return Core.settings.getBool("allow-custom", !headless);
|
||||
}
|
||||
|
||||
public void setCustomClients(boolean allowed){
|
||||
Core.settings.put("allow-custom", allowed);
|
||||
Core.settings.save();
|
||||
return Config.allowCustomClients.bool();
|
||||
}
|
||||
|
||||
/** Call when a player joins to update their information here. */
|
||||
@@ -219,11 +205,7 @@ public class Administration{
|
||||
}
|
||||
|
||||
public boolean isWhitelistEnabled(){
|
||||
return Core.settings.getBool("whitelist", false);
|
||||
}
|
||||
|
||||
public void setWhitelist(boolean enabled){
|
||||
Core.settings.putSave("whitelist", enabled);
|
||||
return Config.whitelist.bool();
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(String id, String usid){
|
||||
@@ -333,6 +315,80 @@ public class Administration{
|
||||
whitelist = Core.settings.getObject("whitelisted", Array.class, Array::new);
|
||||
}
|
||||
|
||||
/** Server configuration definition. Each config value can be a string, boolean or number. */
|
||||
public enum Config{
|
||||
name("The server name as displayed on clients.", "Server", "servername"),
|
||||
port("The port to host on.", Vars.port),
|
||||
autoUpdate("Whether to auto-update and exit when a new bleeding-edge update arrives.", false),
|
||||
startCommands("Commands run at startup. This should be a comma-separated list.", ""),
|
||||
crashReport("Whether to send crash reports.", false, "crashreport"),
|
||||
logging("Whether to log everything to files.", true),
|
||||
strict("Whether strict mode is on - corrects positions and prevents duplicate UUIDs.", true),
|
||||
socketInput("Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),
|
||||
socketInputPort("The port for socket input.", 6859, () -> Events.fire(Trigger.socketConfigChanged)),
|
||||
socketInputAddress("The bind address for socket input.", "localhost", () -> Events.fire(Trigger.socketConfigChanged)),
|
||||
allowCustomClients("Whether custom clients are allowed to connect.", !headless, "allow-custom"),
|
||||
whitelist("Whether the whitelist is used.", false);
|
||||
|
||||
public static final Config[] all = values();
|
||||
|
||||
public final Object defaultValue;
|
||||
public final String key, description;
|
||||
final Runnable changed;
|
||||
|
||||
Config(String description, Object def){
|
||||
this(description, def, null, null);
|
||||
}
|
||||
|
||||
Config(String description, Object def, String key){
|
||||
this(description, def, key, null);
|
||||
}
|
||||
|
||||
Config(String description, Object def, Runnable changed){
|
||||
this(description, def, null, changed);
|
||||
}
|
||||
|
||||
Config(String description, Object def, String key, Runnable changed){
|
||||
this.description = description;
|
||||
this.key = key == null ? name() : key;
|
||||
this.defaultValue = def;
|
||||
this.changed = changed == null ? () -> {} : changed;
|
||||
}
|
||||
|
||||
public boolean isNum(){
|
||||
return defaultValue instanceof Integer;
|
||||
}
|
||||
|
||||
public boolean isBool(){
|
||||
return defaultValue instanceof Boolean;
|
||||
}
|
||||
|
||||
public boolean isString(){
|
||||
return defaultValue instanceof String;
|
||||
}
|
||||
|
||||
public Object get(){
|
||||
return Core.settings.get(key, defaultValue);
|
||||
}
|
||||
|
||||
public boolean bool(){
|
||||
return Core.settings.getBool(key, (Boolean)defaultValue);
|
||||
}
|
||||
|
||||
public int num(){
|
||||
return Core.settings.getInt(key, (Integer)defaultValue);
|
||||
}
|
||||
|
||||
public String string(){
|
||||
return Core.settings.getString(key, (String)defaultValue);
|
||||
}
|
||||
|
||||
public void set(Object value){
|
||||
Core.settings.putSave(key, value);
|
||||
changed.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Serialize
|
||||
public static class PlayerInfo{
|
||||
public String id;
|
||||
|
||||
173
core/src/mindustry/net/BeControl.java
Normal file
173
core/src/mindustry/net/BeControl.java
Normal file
@@ -0,0 +1,173 @@
|
||||
package mindustry.net;
|
||||
|
||||
import arc.*;
|
||||
import arc.Net.*;
|
||||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Handles control of bleeding edge builds. */
|
||||
public class BeControl{
|
||||
private static final int updateInterval = 60 * 2;
|
||||
|
||||
private AsyncExecutor executor = new AsyncExecutor(1);
|
||||
private boolean checkUpdates = true;
|
||||
private boolean updateAvailable;
|
||||
private String updateUrl;
|
||||
private int updateBuild;
|
||||
|
||||
/** @return whether this is a bleeding edge build. */
|
||||
public boolean active(){
|
||||
return Version.type.equals("bleeding-edge");
|
||||
}
|
||||
|
||||
public BeControl(){
|
||||
if(active()){
|
||||
Timer.schedule(() -> {
|
||||
if(checkUpdates && !mobile){
|
||||
checkUpdate(t -> {});
|
||||
}
|
||||
}, 1, updateInterval);
|
||||
}
|
||||
}
|
||||
|
||||
/** asynchronously checks for updates. */
|
||||
public void checkUpdate(Boolc done){
|
||||
Core.net.httpGet("https://api.github.com/repos/Anuken/MindustryBuilds/releases/latest", res -> {
|
||||
if(res.getStatus() == HttpStatus.OK){
|
||||
Jval val = Jval.read(res.getResultAsString());
|
||||
int newBuild = Strings.parseInt(val.getString("tag_name", "0"));
|
||||
if(newBuild > Version.build){
|
||||
Jval asset = val.get("assets").asArray().find(v -> v.getString("name", "").startsWith(headless ? "Mindustry-BE-Server" : "Mindustry-BE-Desktop"));
|
||||
String url = asset.getString("browser_download_url", "");
|
||||
updateAvailable = true;
|
||||
updateBuild = newBuild;
|
||||
updateUrl = url;
|
||||
showUpdateDialog();
|
||||
Core.app.post(() -> done.get(true));
|
||||
}else{
|
||||
Core.app.post(() -> done.get(false));
|
||||
}
|
||||
}else{
|
||||
Core.app.post(() -> done.get(false));
|
||||
}
|
||||
}, error -> {
|
||||
if(!headless){
|
||||
ui.showException(error);
|
||||
}else{
|
||||
error.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** @return whether a new update is available */
|
||||
public boolean isUpdateAvailable(){
|
||||
return updateAvailable;
|
||||
}
|
||||
|
||||
/** shows the dialog for updating the game on desktop, or a prompt for doing so on the server */
|
||||
public void showUpdateDialog(){
|
||||
if(!updateAvailable) return;
|
||||
|
||||
if(!headless){
|
||||
checkUpdates = false;
|
||||
ui.showCustomConfirm(Core.bundle.format("be.update", "") + " " + updateBuild, "$be.update.confirm", "$ok", "$be.ignore", () -> {
|
||||
boolean[] cancel = {false};
|
||||
float[] progress = {0};
|
||||
int[] length = {0};
|
||||
Fi file = bebuildDirectory.child("client-be-" + updateBuild + ".jar");
|
||||
|
||||
FloatingDialog dialog = new FloatingDialog("$be.updating");
|
||||
download(updateUrl, file, i -> length[0] = i, v -> progress[0] = v, () -> cancel[0], () -> {
|
||||
try{
|
||||
Runtime.getRuntime().exec(new String[]{"java", "-DlastBuild=" + Version.build, "-Dberestart", "-jar", file.absolutePath()});
|
||||
System.exit(0);
|
||||
}catch(IOException e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}, e -> {
|
||||
dialog.hide();
|
||||
ui.showException(e);
|
||||
});
|
||||
|
||||
dialog.cont.add(new Bar(() -> length[0] == 0 ? Core.bundle.get("be.updating") : (int)(progress[0] * length[0]) / 1024/ 1024 + "/" + length[0]/1024/1024 + " MB", () -> Pal.accent, () -> progress[0])).width(400f).height(70f);
|
||||
dialog.buttons.addImageTextButton("$cancel", Icon.cancelSmall, () -> {
|
||||
cancel[0] = true;
|
||||
dialog.hide();
|
||||
}).size(210f, 64f);
|
||||
dialog.setFillParent(false);
|
||||
dialog.show();
|
||||
}, () -> checkUpdates = false);
|
||||
}else{
|
||||
Log.info("&lcA new update is available: &lyBleeding Edge build {0}", updateBuild);
|
||||
if(Config.autoUpdate.bool()){
|
||||
Log.info("&lcAuto-downloading next version...");
|
||||
|
||||
try{
|
||||
//download new file from github
|
||||
Fi source = Fi.get(BeControl.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
Fi dest = source.sibling("server-be-" + updateBuild + ".jar");
|
||||
|
||||
download(updateUrl, dest,
|
||||
len -> Core.app.post(() -> Log.info("&ly| Size: {0} MB.", Strings.fixed((float)len / 1024 / 1024, 2))),
|
||||
progress -> {},
|
||||
() -> false,
|
||||
() -> Core.app.post(() -> {
|
||||
netServer.kickAll(KickReason.serverRestarting);
|
||||
Threads.sleep(32);
|
||||
|
||||
Log.info("&lcVersion downloaded, exiting. Note that if you are not using a auto-restart script, the server will not restart automatically.");
|
||||
//replace old file with new
|
||||
dest.copyTo(source);
|
||||
dest.delete();
|
||||
System.exit(2); //this will cause a restart if using the script
|
||||
}),
|
||||
Throwable::printStackTrace);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
checkUpdates = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void download(String furl, Fi dest, Intc length, Floatc progressor, Boolp canceled, Runnable done, Cons<Throwable> error){
|
||||
executor.submit(() -> {
|
||||
try{
|
||||
HttpURLConnection con = (HttpURLConnection)new URL(furl).openConnection();
|
||||
BufferedInputStream in = new BufferedInputStream(con.getInputStream());
|
||||
OutputStream out = dest.write(false, 4096);
|
||||
|
||||
byte[] data = new byte[4096];
|
||||
long size = con.getContentLength();
|
||||
long counter = 0;
|
||||
length.get((int)size);
|
||||
int x;
|
||||
while((x = in.read(data, 0, data.length)) >= 0 && !canceled.get()){
|
||||
counter += x;
|
||||
progressor.get((float)counter / (float)size);
|
||||
out.write(data, 0, x);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
if(!canceled.get()) done.run();
|
||||
}catch(Throwable e){
|
||||
error.get(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,9 @@ public class CrashSender{
|
||||
exception.printStackTrace();
|
||||
|
||||
//don't create crash logs for custom builds, as it's expected
|
||||
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))) return;
|
||||
if(Version.build == -1 || (System.getProperty("user.name").equals("anuke") && "release".equals(Version.modifier))){
|
||||
ret();
|
||||
}
|
||||
|
||||
//attempt to load version regardless
|
||||
if(Version.number == 0){
|
||||
@@ -63,7 +65,7 @@ public class CrashSender{
|
||||
try{
|
||||
//check crash report setting
|
||||
if(!Core.settings.getBool("crashreport", true)){
|
||||
return;
|
||||
ret();
|
||||
}
|
||||
}catch(Throwable ignored){
|
||||
//if there's no settings init we don't know what the user wants but chances are it's an important crash, so send it anyway
|
||||
@@ -72,14 +74,14 @@ public class CrashSender{
|
||||
try{
|
||||
//check any mods - if there are any, don't send reports
|
||||
if(Vars.mods != null && !Vars.mods.list().isEmpty()){
|
||||
return;
|
||||
ret();
|
||||
}
|
||||
}catch(Throwable ignored){
|
||||
}
|
||||
|
||||
//do not send exceptions that occur for versions that can't be parsed
|
||||
if(Version.number == 0){
|
||||
return;
|
||||
ret();
|
||||
}
|
||||
|
||||
boolean netActive = false, netServer = false;
|
||||
@@ -130,12 +132,16 @@ public class CrashSender{
|
||||
while(!sent[0]){
|
||||
Thread.sleep(30);
|
||||
}
|
||||
}catch(InterruptedException ignored){
|
||||
}
|
||||
}catch(InterruptedException ignored){}
|
||||
}catch(Throwable death){
|
||||
death.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
ret();
|
||||
}
|
||||
|
||||
private static void ret(){
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private static void httpPost(String url, String content, Cons<HttpResponse> success, Cons<Throwable> failure){
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.net;
|
||||
|
||||
import mindustry.*;
|
||||
import mindustry.game.*;
|
||||
|
||||
public class Host{
|
||||
@@ -11,7 +12,7 @@ public class Host{
|
||||
public final int version;
|
||||
public final String versionType;
|
||||
public final Gamemode mode;
|
||||
public int ping;
|
||||
public int ping, port = Vars.port;
|
||||
|
||||
public Host(String name, String address, String mapname, int wave, int players, int version, String versionType, Gamemode mode, int playerLimit){
|
||||
this.name = name;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package mindustry.net;
|
||||
|
||||
import arc.*;
|
||||
import arc.util.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.type.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.maps.Map;
|
||||
import mindustry.net.Administration.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
@@ -62,7 +62,7 @@ public class NetworkIO{
|
||||
}
|
||||
|
||||
public static ByteBuffer writeServerData(){
|
||||
String name = (headless ? Core.settings.getString("servername") : player.name);
|
||||
String name = (headless ? Config.name.string() : player.name);
|
||||
String map = world.getMap() == null ? "None" : world.getMap().name();
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(256);
|
||||
|
||||
@@ -15,7 +15,8 @@ public class Packets{
|
||||
|
||||
public enum KickReason{
|
||||
kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick,
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch, whitelist, playerLimit;
|
||||
nameInUse, idInUse, nameEmpty, customClient, serverClose, vote, typeMismatch,
|
||||
whitelist, playerLimit, serverRestarting;
|
||||
|
||||
public final boolean quiet;
|
||||
|
||||
|
||||
@@ -288,7 +288,12 @@ public class JoinDialog extends FloatingDialog{
|
||||
local.table(Tex.button, t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX();
|
||||
net.discoverServers(this::addLocalHost, this::finishLocalHosts);
|
||||
for(String host : defaultServers){
|
||||
net.pingHost(host, port, this::addLocalHost, e -> {});
|
||||
String resaddress = host.contains(":") ? host.split(":")[0] : host;
|
||||
int resport = host.contains(":") ? Strings.parseInt(host.split(":")[1]) : port;
|
||||
net.pingHost(resaddress, resport, res -> {
|
||||
res.port = resport;
|
||||
addLocalHost(res);
|
||||
}, e -> {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,7 +319,7 @@ public class JoinDialog extends FloatingDialog{
|
||||
|
||||
local.row();
|
||||
|
||||
TextButton button = local.addButton("", Styles.cleart, () -> safeConnect(host.address, port, host.version))
|
||||
TextButton button = local.addButton("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version))
|
||||
.width(w).pad(5f).get();
|
||||
button.clearChildren();
|
||||
buildServer(host, button);
|
||||
@@ -362,7 +367,7 @@ public class JoinDialog extends FloatingDialog{
|
||||
servers = Core.settings.getObject("server-list", Array.class, Array::new);
|
||||
|
||||
//get servers
|
||||
Core.net.httpGet(serverJsonURL, result -> {
|
||||
Core.net.httpGet(becontrol.active() ? serverJsonBeURL : serverJsonURL, result -> {
|
||||
try{
|
||||
Jval val = Jval.read(result.getResultAsString());
|
||||
Core.app.post(() -> {
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ModsDialog extends FloatingDialog{
|
||||
|
||||
buttons.row();
|
||||
|
||||
buttons.addImageTextButton("$mods.guide", Icon.wiki,
|
||||
buttons.addImageTextButton("$mods.guide", Icon.link,
|
||||
() -> Core.net.openURI(modGuideURL))
|
||||
.size(210, 64f);
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ public class SchematicsDialog extends FloatingDialog{
|
||||
setup();
|
||||
ui.showInfoFade("$schematic.saved");
|
||||
showInfo(s);
|
||||
}catch(Exception e){
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || !Core.app.getClipboardText().startsWith(schematicBaseStart));
|
||||
|
||||
@@ -229,6 +229,7 @@ public class SettingsMenuDialog extends SettingsDialog{
|
||||
game.checkPref("savecreate", true);
|
||||
game.checkPref("blockreplace", true);
|
||||
game.checkPref("conveyorpathfinding", true);
|
||||
game.checkPref("coreselect", false);
|
||||
game.checkPref("hints", true);
|
||||
if(!mobile){
|
||||
game.checkPref("buildautopause", false);
|
||||
|
||||
@@ -59,6 +59,18 @@ public class MenuFragment extends Fragment{
|
||||
if(mobile){
|
||||
parent.fill(c -> c.bottom().left().addButton("", Styles.infot, ui.about::show).size(84, 45));
|
||||
parent.fill(c -> c.bottom().right().addButton("", Styles.discordt, ui.discord::show).size(84, 45));
|
||||
}else if(becontrol.active()){
|
||||
parent.fill(c -> c.bottom().right().addImageTextButton("$be.check", Icon.refreshSmall, () -> {
|
||||
ui.loadfrag.show();
|
||||
becontrol.checkUpdate(result -> {
|
||||
ui.loadfrag.hide();
|
||||
if(!result){
|
||||
ui.showInfo("$be.noupdates");
|
||||
}
|
||||
});
|
||||
}).size(200, 60).update(t -> {
|
||||
t.getLabel().setColor(becontrol.isUpdateAvailable() ? Tmp.c1.set(Color.white).lerp(Pal.accent, Mathf.absin(5f, 1f)) : Color.white);
|
||||
}));
|
||||
}
|
||||
|
||||
String versionText = "[#ffffffba]" + ((Version.build == -1) ? "[#fc8140aa]custom build" : (Version.type.equals("official") ? Version.modifier : Version.type) + " build " + Version.build + (Version.revision == 0 ? "" : "." + Version.revision));
|
||||
|
||||
@@ -257,7 +257,7 @@ public class Tile implements Position, TargetTrait{
|
||||
}
|
||||
|
||||
public boolean solid(){
|
||||
return block.solid || block.isSolidFor(this) || (isLinked() && link().solid());
|
||||
return block.solid || block.isSolidFor(this) || (isLinked() && link() != this && link().solid());
|
||||
}
|
||||
|
||||
public boolean breakable(){
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -37,7 +38,7 @@ public class ForceProjector extends Block{
|
||||
private static ForceProjector paramBlock;
|
||||
private static ForceEntity paramEntity;
|
||||
private static Cons<AbsorbTrait> shieldConsumer = trait -> {
|
||||
if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && paramBlock.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){
|
||||
if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && Intersector.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){
|
||||
trait.absorb();
|
||||
Effects.effect(Fx.absorb, trait);
|
||||
paramEntity.hit = 1f;
|
||||
@@ -111,17 +112,6 @@ public class ForceProjector extends Block{
|
||||
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, entity.efficiency(), 0.1f);
|
||||
|
||||
/*
|
||||
if(entity.power.status < relativePowerDraw){
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f);
|
||||
entity.power.status = 0f;
|
||||
if(entity.warmup <= 0.09f){
|
||||
entity.broken = true;
|
||||
}
|
||||
}else{
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f);
|
||||
}*/
|
||||
|
||||
if(entity.buildup > 0){
|
||||
float scale = !entity.broken ? cooldownNormal : cooldownBrokenBase;
|
||||
ConsumeLiquidFilter cons = consumes.get(ConsumeType.liquid);
|
||||
@@ -159,13 +149,6 @@ public class ForceProjector extends Block{
|
||||
return (radius + entity.phaseHeat * phaseRadiusBoost) * entity.radscl;
|
||||
}
|
||||
|
||||
boolean isInsideHexagon(float x0, float y0, float d, float x, float y){
|
||||
float dx = Math.abs(x - x0) / d;
|
||||
float dy = Math.abs(y - y0) / d;
|
||||
float a = 0.25f * Mathf.sqrt3;
|
||||
return (dy <= a) && (a * dx + 0.25 * dy <= 0.5 * a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Tile tile){
|
||||
super.draw(tile);
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.Core;
|
||||
import arc.graphics.g2d.Draw;
|
||||
import arc.graphics.g2d.TextureRegion;
|
||||
import arc.math.Mathf;
|
||||
import arc.util.*;
|
||||
import mindustry.content.Fx;
|
||||
import mindustry.content.Liquids;
|
||||
import mindustry.entities.Effects;
|
||||
@@ -51,8 +52,8 @@ public class SolidPump extends Pump{
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
bars.add("efficiency", entity -> new Bar(() ->
|
||||
Core.bundle.formatFloat("bar.efficiency",
|
||||
((((SolidPumpEntity)entity).boost + 1f) * ((SolidPumpEntity)entity).warmup) * 100 * percentSolid(entity.tile.x, entity.tile.y), 1),
|
||||
Core.bundle.formatFloat("bar.pumpspeed",
|
||||
((SolidPumpEntity)entity).lastPump / Time.delta() * 60, 1),
|
||||
() -> Pal.ammo,
|
||||
() -> ((SolidPumpEntity)entity).warmup));
|
||||
}
|
||||
@@ -104,11 +105,13 @@ public class SolidPump extends Pump{
|
||||
if(tile.entity.cons.valid() && typeLiquid(tile) < liquidCapacity - 0.001f){
|
||||
float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction * entity.efficiency());
|
||||
tile.entity.liquids.add(result, maxPump);
|
||||
entity.lastPump = maxPump;
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f);
|
||||
if(Mathf.chance(entity.delta() * updateEffectChance))
|
||||
Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f));
|
||||
}else{
|
||||
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.02f);
|
||||
entity.lastPump = 0f;
|
||||
}
|
||||
|
||||
entity.pumpTime += entity.warmup * entity.delta();
|
||||
@@ -153,5 +156,6 @@ public class SolidPump extends Pump{
|
||||
public float warmup;
|
||||
public float pumpTime;
|
||||
public float boost;
|
||||
public float lastPump;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user