diff --git a/build.gradle b/build.gradle index 13ee44b98f..6aa109db1e 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ allprojects { appName = 'Mindustry' gdxVersion = '1.9.8' roboVMVersion = '2.3.0' - uCoreVersion = 'dec41336067c013f04f0e17db3b06e24d3c11f6a' + uCoreVersion = '7a77659cc5bb22c586d73cde6e21b854962e7f64' getVersionString = { String buildVersion = getBuildVersion() diff --git a/core/src/io/anuke/mindustry/entities/units/types/Drone.java b/core/src/io/anuke/mindustry/entities/units/types/Drone.java index 244f5ef455..1af4018725 100644 --- a/core/src/io/anuke/mindustry/entities/units/types/Drone.java +++ b/core/src/io/anuke/mindustry/entities/units/types/Drone.java @@ -79,12 +79,14 @@ public class Drone extends FlyingUnit implements BuilderTrait{ } //if it's missing requirements, try and mine them - for(ItemStack stack : entity.recipe.requirements){ - if(!core.items.has(stack.item, stack.amount) && type.toMine.contains(stack.item)){ - targetItem = stack.item; - getPlaceQueue().clear(); - setState(mine); - return; + if(entity.recipe != null){ + for(ItemStack stack : entity.recipe.requirements){ + if(!core.items.has(stack.item, stack.amount) && type.toMine.contains(stack.item)){ + targetItem = stack.item; + getPlaceQueue().clear(); + setState(mine); + return; + } } } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java index 1ba76a20ab..d8f39dc993 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/UnlocksDialog.java @@ -6,10 +6,10 @@ import io.anuke.mindustry.game.Content; import io.anuke.mindustry.game.UnlockableContent; import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.type.ContentType; +import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.scene.event.HandCursorListener; import io.anuke.ucore.scene.ui.Image; import io.anuke.ucore.scene.ui.ScrollPane; -import io.anuke.ucore.scene.ui.Tooltip; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.utils.UIUtils; @@ -65,10 +65,7 @@ public class UnlocksDialog extends FloatingDialog{ if(control.unlocks.isUnlocked(unlock)){ image.clicked(() -> Vars.ui.content.show(unlock)); - image.addListener(new Tooltip<>(new Table("clear"){{ - add(unlock.localizedName()); - margin(4); - }})); + StatValue.addToolTip(image, unlock); } if((++count) % maxWidth == 0){ diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java index d02f701ab1..f9d4a8aba2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -37,27 +37,62 @@ public class PowerGraph{ lastFrameUpdated = threads.getFrameID(); + boolean charge = false; + float totalInput = 0f; + float bufferInput = 0f; for(Tile producer : producers){ - totalInput += producer.entity.power.amount; + if (producer.block().consumesPower) { + bufferInput += producer.entity.power.amount; + } else { + totalInput += producer.entity.power.amount; + } } float maxOutput = 0f; + float bufferOutput = 0f; for(Tile consumer : consumers){ - maxOutput += consumer.block().powerCapacity - consumer.entity.power.amount; + if (consumer.block().outputsPower) { + bufferOutput += consumer.block().powerCapacity - consumer.entity.power.amount; + } else { + maxOutput += consumer.block().powerCapacity - consumer.entity.power.amount; + } } - if (totalInput <= 0.0001f || maxOutput <= 0.0001f) { + if (maxOutput < totalInput) { + charge = true; + } + + if (totalInput + bufferInput <= 0.0001f || maxOutput + bufferOutput <= 0.0001f) { return; } - float inputUsed = Math.min(maxOutput / totalInput, 1f); + float bufferUsed = 0; + if (charge) { + bufferUsed = Math.min((totalInput - maxOutput) / bufferOutput, 1f); + } else { + bufferUsed = Math.min((maxOutput - totalInput) / bufferInput, 1f); + } + + float inputUsed = charge ? Math.min((maxOutput + bufferOutput) / totalInput, 1f) : 1f; for(Tile producer : producers){ + if (producer.block().consumesPower) { + if (!charge) { + producer.entity.power.amount -= producer.entity.power.amount * bufferUsed; + } + continue; + } producer.entity.power.amount -= producer.entity.power.amount * inputUsed; } - float outputSatisfied = Math.min(totalInput / maxOutput, 1f); + float outputSatisfied = charge ? 1f : Math.min((totalInput + bufferInput) / maxOutput, 1f); for(Tile consumer : consumers){ + if (consumer.block().outputsPower) { + if (charge) { + consumer.entity.power.amount += (consumer.block().powerCapacity - consumer.entity.power.amount) * bufferUsed; + } + continue; + } consumer.entity.power.amount += (consumer.block().powerCapacity - consumer.entity.power.amount) * outputSatisfied; } } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java b/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java index 65eacb6ef7..e2854906dd 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Cultivator.java @@ -42,6 +42,7 @@ public class Cultivator extends Drill{ stats.remove(BlockStat.drillTier); stats.add(BlockStat.drillTier, table -> { table.addImage("grass1").size(8 * 3).padBottom(3).padTop(3); + // TODO: find out localized name and add tool tip }); } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Drill.java b/core/src/io/anuke/mindustry/world/blocks/production/Drill.java index 12a6635eb8..ac641ca4f7 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Drill.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Drill.java @@ -16,11 +16,14 @@ import io.anuke.mindustry.world.consumers.ConsumeLiquid; import io.anuke.mindustry.world.meta.BlockGroup; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.StatUnit; +import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Threads; @@ -133,7 +136,8 @@ public class Drill extends Block{ for(int i = 0; i < list.size; i++){ Item item = list.get(i); - table.addImage(item.name + "1").size(8 * 3).padRight(2).padLeft(2).padTop(3).padBottom(3); + Cell imageCell = table.addImage(item.name + "1").size(8 * 3).padRight(2).padLeft(2).padTop(3).padBottom(3); + StatValue.addToolTip(imageCell.getElement(), item); if(i != list.size - 1){ table.add("/"); } diff --git a/core/src/io/anuke/mindustry/world/meta/StatValue.java b/core/src/io/anuke/mindustry/world/meta/StatValue.java index 5b95ff6e4d..ef3e51acef 100644 --- a/core/src/io/anuke/mindustry/world/meta/StatValue.java +++ b/core/src/io/anuke/mindustry/world/meta/StatValue.java @@ -1,5 +1,10 @@ package io.anuke.mindustry.world.meta; +import io.anuke.mindustry.game.UnlockableContent; +import io.anuke.ucore.scene.Element; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.Tooltip; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; /** @@ -7,8 +12,38 @@ import io.anuke.ucore.scene.ui.layout.Table; */ public interface StatValue{ /** - * This method should all elements necessary to display this stat to the specified table. + * This method should provide all elements necessary to display this stat to the specified table. * For example, a stat that is just text would add label to the table. */ void display(Table table); + + /** + * This method adds an icon image together with a tool tip which contains the name of the item. + * @param table the table to add the image cell to. + * @param item The item which provides the tool tip content. + * @return the image cell which was created. The cell is not yet sized or padded. + */ + static Cell addImageWithToolTip(Table table, UnlockableContent item){ + + // Create a table cell with a new image as provided by the item + Cell imageCell = table.addImage(item.getContentIcon()); + + // Retrieve the image and add a tool tip with the item's name + addToolTip(imageCell.getElement(), item); + + // Return the table cell for further processing (sizing, padding, ...) + return imageCell; + } + + /** + * Adds a tool tip containing the item's localized name to the given element. + * @param element The element to assign the tool tip to. + * @param item The item which provides the tool tip content. + */ + static void addToolTip(Element element, UnlockableContent item){ + element.addListener(new Tooltip<>(new Table("clear"){{ + add(item.localizedName()); + margin(4); + }})); + } } diff --git a/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java b/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java index 79348446ba..669f5baa2c 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/ItemFilterValue.java @@ -4,6 +4,8 @@ import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.function.Predicate; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; import static io.anuke.mindustry.Vars.*; @@ -24,7 +26,12 @@ public class ItemFilterValue implements StatValue{ for(int i = 0; i < list.size; i++){ Item item = list.get(i); - table.addImage(item.region).size(8 * 3).padRight(2).padLeft(2); + + Cell imageCell = table.addImage(item.region); + imageCell.size(8 * 3).padRight(2).padLeft(2); + + StatValue.addToolTip(imageCell.getElement(), item); + if(i != list.size - 1){ table.add("/"); } diff --git a/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java b/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java index 9a236306a3..7cb237296f 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/ItemListValue.java @@ -5,6 +5,9 @@ import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.ui.ItemImage; import io.anuke.mindustry.world.meta.ContentStatValue; +import io.anuke.mindustry.world.meta.StatValue; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; public class ItemListValue implements ContentStatValue{ @@ -38,11 +41,17 @@ public class ItemListValue implements ContentStatValue{ public void display(Table table){ if(items != null){ for(Item item : items){ - table.addImage(item.region).size(8 * 3).padRight(5); + Cell imageCell = table.addImage(item.region); + imageCell.size(8 * 3).padRight(5); + + StatValue.addToolTip(imageCell.getElement(), item); } }else{ for(ItemStack stack : stacks){ - table.add(new ItemImage(stack)).size(8 * 3).padRight(5); + ItemImage image = new ItemImage(stack); + table.add(image).size(8 * 3).padRight(5); + + StatValue.addToolTip(image, stack.item); } } } diff --git a/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java b/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java index d42312c4d1..269c537dc5 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/ItemValue.java @@ -5,6 +5,7 @@ import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.ui.ItemImage; import io.anuke.mindustry.world.meta.ContentStatValue; +import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.scene.ui.layout.Table; public class ItemValue implements ContentStatValue{ @@ -22,6 +23,8 @@ public class ItemValue implements ContentStatValue{ @Override public void display(Table table){ //TODO better implementation, quantity support - table.add(new ItemImage(item)).size(8 * 3); + ItemImage image = new ItemImage(item); + table.add(image).size(8 * 3); + StatValue.addToolTip(image, item.item); } } diff --git a/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java b/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java index 9a3417581f..9dc6a6bd0b 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/LiquidFilterValue.java @@ -4,6 +4,9 @@ import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.meta.StatValue; import io.anuke.ucore.function.Predicate; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.Tooltip; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; import static io.anuke.mindustry.Vars.*; @@ -24,7 +27,10 @@ public class LiquidFilterValue implements StatValue{ for(int i = 0; i < list.size; i++){ Liquid item = list.get(i); - table.addImage(item.getContentIcon()).size(8 * 3).padRight(2).padLeft(2).padTop(2).padBottom(2); + + Cell imageCell = StatValue.addImageWithToolTip(table, item); + imageCell.size(8 * 3).padRight(2).padLeft(2).padTop(2).padBottom(2); + if(i != list.size - 1){ table.add("/"); } diff --git a/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java b/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java index bdee87897e..d4d6734511 100644 --- a/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java +++ b/core/src/io/anuke/mindustry/world/meta/values/LiquidValue.java @@ -3,6 +3,9 @@ package io.anuke.mindustry.world.meta.values; import io.anuke.mindustry.game.UnlockableContent; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.meta.ContentStatValue; +import io.anuke.mindustry.world.meta.StatValue; +import io.anuke.ucore.scene.ui.Image; +import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; public class LiquidValue implements ContentStatValue{ @@ -19,6 +22,7 @@ public class LiquidValue implements ContentStatValue{ @Override public void display(Table table){ - table.addImage(liquid.getContentIcon()).size(8 * 3); + Cell imageCell = StatValue.addImageWithToolTip(table, liquid); + imageCell.size(8 * 3); } } diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 1688653ecd..9ecc29d0d3 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.server; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.Timer; @@ -41,12 +42,18 @@ import static io.anuke.ucore.util.Log.*; public class ServerControl extends Module{ private static final int roundExtraTime = 12; + //in bytes: 512 kb is max + private static final int maxLogLength = 1024 * 512; private final CommandHandler handler = new CommandHandler(""); + private final FileHandle logFolder = Gdx.files.local("logs/"); + + private FileHandle currentLogFile; private int gameOvers; private boolean inExtraRound; private Task lastTask; + public ServerControl(String[] args){ Settings.defaultList( "shufflemode", "normal", @@ -56,7 +63,8 @@ public class ServerControl extends Module{ "sector_y", 1, "shuffle", true, "crashreport", false, - "port", port + "port", port, + "logging", true ); Log.setLogger(new LogHandler(){ @@ -64,22 +72,27 @@ public class ServerControl extends Module{ @Override public void info(String text, Object... args){ - print("&lg&fb" + "[INFO] " + format(text, args)); + print("&lg&fb" + "[INFO] " + text, args); } @Override public void err(String text, Object... args){ - print("&lr&fb" + "[ERR!] " + format(text, args)); + print("&lr&fb" + "[ERR!] " + text, args); } @Override public void warn(String text, Object... args){ - print("&ly&fb" + "[WARN] " + format(text, args)); + print("&ly&fb" + "[WARN] " + text, args); } @Override public void print(String text, Object... args){ - System.out.println("[" + dateTime.format(LocalDateTime.now()) + "] " + format(text + "&fr", args)); + String result = "[" + dateTime.format(LocalDateTime.now()) + "] " + format(text + "&fr", args); + System.out.println(result); + + if(Settings.getBool("logging")){ + logToFile("[" + dateTime.format(LocalDateTime.now()) + "] " + format(text + "&fr", false, args)); + } } }); @@ -311,7 +324,7 @@ public class ServerControl extends Module{ return; } - Call.sendMessage("[GRAY][[Server]:[] " + arg[0]); + Call.sendMessage("[scarlet][[Server]:[] " + arg[0]); info("&lyServer: &lb{0}", arg[0]); }); @@ -357,6 +370,13 @@ public class ServerControl extends Module{ info("Crash reporting is now {0}.", value ? "on" : "off"); }); + handler.register("logging", "", "Disables or enables server logs", arg -> { + boolean value = arg[0].equalsIgnoreCase("on"); + Settings.putBool("logging", value); + Settings.save(); + info("Logging is now {0}.", value ? "on" : "off"); + }); + handler.register("strict", "", "Disables or enables strict mode", arg -> { boolean value = arg[0].equalsIgnoreCase("on"); netServer.admins.setStrict(value); @@ -400,6 +420,7 @@ public class ServerControl extends Module{ Player target = playerGroup.find(p -> p.name.equals(arg[0])); if(target != null){ + Call.sendMessage("[scarlet] " + target.name + " has been kicked by the server."); netServer.kick(target.con.id, KickReason.kick); info("It is done."); }else{ @@ -425,6 +446,13 @@ public class ServerControl extends Module{ }else{ err("Invalid type."); } + + for(Player player : playerGroup.all()){ + if(netServer.admins.isIDBanned(player.uuid)){ + Call.sendMessage("[scarlet] " + player.name + " has been banned."); + netServer.kick(player.con.id, KickReason.banned); + } + } }); handler.register("bans", "List all banned IPs and IDs.", arg -> { @@ -724,4 +752,23 @@ public class ServerControl extends Module{ state.set(State.menu); } } + + private void logToFile(String text){ + if(currentLogFile != null && currentLogFile.length() > maxLogLength){ + String date = DateTimeFormatter.ofPattern("MM-dd-yyyy | HH:mm:ss").format(LocalDateTime.now()); + currentLogFile.writeString("[End of log file. Date: "+ date + "]\n", true); + currentLogFile = null; + } + + if(currentLogFile == null){ + int i = 0; + while(logFolder.child("log-" + i + ".txt").length() >= maxLogLength){ + i ++; + } + + currentLogFile = logFolder.child("log-" + i + ".txt"); + } + + currentLogFile.writeString(text + "\n", true); + } }