diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index c453591311..0843d8d3b5 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -28,7 +28,7 @@ public class BlockIndexer{ private int quadWidth, quadHeight; /** Stores all ore quadrants on the map. Maps ID to qX to qY to a list of tiles with that ore. */ - private IntSeq[][][] ores, wallOres; // FINISHME: Current wall ore indexing is cursed + private IntSeq[][][] ores, wallOres; /** Stores all damaged tile entities by team. */ private Seq[] damagedTiles = new Seq[Team.all.length]; /** All ores available on this map. */ diff --git a/core/src/mindustry/client/Commands.kt b/core/src/mindustry/client/Commands.kt index 60783303bb..8d18655721 100644 --- a/core/src/mindustry/client/Commands.kt +++ b/core/src/mindustry/client/Commands.kt @@ -8,6 +8,7 @@ import arc.math.geom.* import arc.struct.* import arc.util.* import arc.util.CommandHandler.* +import arc.util.serialization.* import mindustry.Vars.* import mindustry.ai.types.* import mindustry.client.ClientVars.* @@ -22,6 +23,7 @@ import mindustry.client.utils.* import mindustry.content.* import mindustry.core.* import mindustry.entities.* +import mindustry.game.* import mindustry.gen.* import mindustry.input.* import mindustry.logic.* @@ -39,6 +41,7 @@ import java.math.* import java.security.cert.* import java.time.* import java.time.temporal.* +import java.util.concurrent.* import java.util.regex.* import kotlin.math.* import kotlin.random.* @@ -722,7 +725,7 @@ fun setupCommands() { } } - + // Symbol replacements registerReplace("%", "c", "cursor") { @@ -740,7 +743,7 @@ fun setupCommands() { //FINISHME: add various % for gamerules // Experimentals (and funny commands) - if (Core.settings.getBool("client-experimentals")) { + if (Core.settings.getBool("client-experimentals") || OS.hasProp("policone")) { register("poli", "Spelling is hard. This will make sure you never forget how to spell the plural of poly, you're welcome.") { _, _ -> sendMessage("Unlike a roly-poly whose plural is roly-polies, the plural form of poly is polys. Please remember this, thanks! :)") } @@ -821,6 +824,66 @@ fun setupCommands() { Tmp.r1.set(player.x - range, player.y - range, range * 2, range * 2) undoPlayer(world.tiles.filter { it.getBounds(Tmp.r2).overlaps(Tmp.r1) && it.within(player.x, player.y, range) }, id) } + + register("upload", "This is a terrible idea") { _, player -> // FINISHME: This is a super lazy implementation + val results = ConcurrentLinkedQueue() // Results of the http requests + val pool = Threads.unboundedExecutor("Schematic Upload", 1) + val sb = StringBuilder() + + fun uploadSchematics() { // FINISHME: We really need to handle failed uploads + val str = sb.substring(0, sb.length - 1) // Drop the trailing \n + pool.execute { Http.post("https://cancer-co.de/upload", "text=" + Strings.encode(str)).block { results.add(it.resultAsString) } } + sb.clear() + } + + schematics.all().each { + val b = schematics.writeBase64(it) + if (b.length + 1 > 8_000_000) { // Who in their right mind has a schematic that's over 8 million characters + player.sendMessage("[scarlet]You have an insanely large schematic which will not be uploaded.") + return@each + } + if (sb.length + b.length> 8_000_000) uploadSchematics() + + sb.append(b).append('\n') + } + + if (sb.length > 0) uploadSchematics() // Upload any leftovers + sb.append("[accent]Your schematics have been uploaded: ") + Threads.await(pool) // Wait for all requests to finish before continuing + results.forEach { + val json = Jval.read(it) + sb.append(json.getString("url").substringAfterLast('/')).append(' ') + } + sb.setLength(sb.length - 1) // Remove extra appended space + val ids = sb.toString().substringAfter(": ") + ui.chatfrag.addMsg(sb.toString()).addButton(0, sb.length) { Core.app.clipboardText = ids } + } + + register("view ", "This is an equally terrible idea") { args, player -> // FINISHME: Why did I think this was a good idea? + val pool = Threads.unboundedExecutor("Schematic Download") + val browser = ui.schematicBrowser + val dest = browser.loadedRepositories.get(args[0]) { Seq() }.clear() + + args[1].split(' ').forEach { id -> + pool.execute { Http.get("https://cancer-co.de/raw/$id").block { r -> // FINISHME: Add handling for failed http requests + val str = r.resultAsString + if (str == "Paste not found!") { // FINISHME: Improve messaging for failed loads + player.sendMessage("[scarlet]Failed to load https://cancer-co.de/raw/$id as it was not found.") + return@block + } + val out = Seq() + str.split('\n').forEach { + Log.info(it) + out.add(Schematics.readBase64(it)) + } + Core.app.post { // Do this on the main thread + player.sendMessage("[accent]Finished loading $id") + dest.add(out) + browser.rebuildAll() + } + } } + } + } } } diff --git a/core/src/mindustry/client/navigation/Navigator.kt b/core/src/mindustry/client/navigation/Navigator.kt index 22125e31d1..da3c7053e2 100644 --- a/core/src/mindustry/client/navigation/Navigator.kt +++ b/core/src/mindustry/client/navigation/Navigator.kt @@ -1,6 +1,5 @@ package mindustry.client.navigation -import arc.* import arc.math.geom.* import arc.struct.* import arc.util.* @@ -17,15 +16,9 @@ import mindustry.world.blocks.defense.* /** An abstract class for a navigation algorithm, i.e. A*. */ abstract class Navigator { @JvmField - val map = IntMap() - var lastWp = 0L + var lastTp = 0L private val realObstacles = Seq() // Avoids creating new lists every time navigate is called - init { - Events.on(EventType.WorldLoadEvent::class.java) { - map.clear() - } - } /** Called once upon client loading. */ abstract fun init() @@ -47,6 +40,7 @@ abstract class Navigator { end.clamp(0f, 0f, world.unitWidth().toFloat(), world.unitHeight().toFloat()) val additionalRadius = player.unit().hitSize / 2 + tilesize + // Turrets and units FINISHME: Turrets should probably not use this system if (player.unit().type.targetable(player.unit(), player.team()) && player.unit().type.hittable(player.unit())) { for (turret in obstacles) { if (turret.canHitPlayer() && turret.canShoot()) { @@ -76,37 +70,35 @@ abstract class Navigator { // Shield projectors for (team in state.teams.active) { - for (shield in team.getBuildings(Blocks.shieldProjector).addAll(team.getBuildings(Blocks.largeShieldProjector))) { - realObstacles.add( + if (team == player.team()) continue + arrayOf(team.getBuildings(Blocks.shieldProjector), team.getBuildings(Blocks.largeShieldProjector)).forEach { shields -> + val radius = ((shields.firstOpt()?.block ?: return@forEach) as BaseShield).radius + additionalRadius + for (shield in shields) { + realObstacles.add( Pools.obtain(Circle::class.java) { Circle() }.set( - shield.x, - shield.y, - (shield.block as BaseShield).radius + additionalRadius + shield.x, + shield.y, + radius ) - ) + ) + } } } //Consider respawning at a core - if (Time.timeSinceMillis(lastWp) > 3000 && player.team().cores().any()) { - if (map.size > 0) { // CN auto core tp is different as a plugin allows for some magic... - val closestCore = map.minByOrNull { it.value.dst2(end) }!! - if (player.dst2(closestCore.value) > buildingRange * buildingRange && player.dst2(end) > closestCore.value.dst2(end)) { - lastWp = Time.millis() // Try again in 3s - Call.sendChatMessage("/wp ${closestCore.key}") - } - } else if ( + if (Time.timeSinceMillis(lastTp) > 3000 && player.team().cores().any()) { + if ( player.unit().spawnedByCore && player.unit().stack.amount == 0 && (if(player.unit() is Payloadc) !(player.unit() as Payloadc).hasPayload() else true) - ) { // Everything that isn't CN + ) { val bestCore = player.team().cores().min(Structs.comps(Structs.comparingInt { -it.block.size }, Structs.comparingFloat { it.dst2(end) })) if (player.dst2(bestCore) > buildingRange * buildingRange && player.dst2(end) > bestCore.dst2(end) && player.dst2(bestCore) > player.unit().speed() * player.unit().speed() * 24 * 24) { // don't try to move if we're already close to that core - lastWp = Time.millis() // Try again in 3s FINISHME: Add the Call to the config queue as it uses ratelimit, prevent running this again until 3s after last one is processed + lastTp = Time.millis() // Try again in 3s if (ClientVars.ratelimitRemaining > 1) Call.buildingControlSelect(player, bestCore) } } - if (Time.timeSinceMillis(lastWp) > 3000) lastWp = Time.millis() - 2900 // Didn't tp, try again in .1s + if (Time.timeSinceMillis(lastTp) > 3000) lastTp = Time.millis() - 2900 // Didn't tp, try again in .1s } val avoidFlood = CustomMode.flood() && player.unit().type != UnitTypes.horizon diff --git a/core/src/mindustry/client/ui/SchematicBrowserDialog.java b/core/src/mindustry/client/ui/SchematicBrowserDialog.java index 180b43e53a..490bd1660e 100644 --- a/core/src/mindustry/client/ui/SchematicBrowserDialog.java +++ b/core/src/mindustry/client/ui/SchematicBrowserDialog.java @@ -156,7 +156,7 @@ public void setCullingArea(Rect cullingArea) { rebuildPane = () -> { t[0].clear(); firstSchematic = null; - for (String repo : loadedRepositories.keys()) { + for (String repo : loadedRepositories.keys().toSeq().sort()) { if (hiddenRepositories.contains(repo)) continue; setupRepoUi(t[0], ignoreSymbols.matcher(search.toLowerCase()).replaceAll(""), repo); } @@ -197,7 +197,7 @@ void setupRepoUi(Table table, String searchString, String repo){ ImageButton.ImageButtonStyle style = Styles.emptyi; - buttons.button(Icon.info, style, () -> showInfo(s)).tooltip("@info.title"); + buttons.button(Icon.info, style, () -> ui.schematics.showInfo(s)).tooltip("@info.title"); buttons.button(Icon.upload, style, () -> showExport(s)).tooltip("@editor.export"); buttons.button(Icon.download, style, () -> { ui.showInfoFade("@schematic.saved"); @@ -227,7 +227,7 @@ void setupRepoUi(Table table, String searchString, String repo){ }, () -> { if(sel[0].childrenPressed()) return; if(state.isMenu()){ - showInfo(s); + ui.schematics.showInfo(s); }else{ if(!(state.rules.schematicsAllowed || Core.settings.getBool("forceallowschematics"))){ ui.showInfo("@schematic.disabled"); @@ -256,10 +256,6 @@ void setupRepoUi(Table table, String searchString, String repo){ table.row(); } - public void showInfo(Schematic schematic){ - ui.schematics.info.show(schematic); - } - public void showExport(Schematic s){ BaseDialog dialog = new BaseDialog("@editor.export"); dialog.cont.pane(p -> { @@ -306,7 +302,7 @@ void checkTags(Schematic s){ } } - void rebuildAll(){ + public void rebuildAll(){ tags.clear(); selectedTags.clear(); for (var repo : loadedRepositories.keys()){ @@ -487,12 +483,7 @@ void loadRepositories(){ ui.showErrorMessage(Core.bundle.format("schematic.browser.fail.parse", link, f.name())); } }); - if (loadedRepositories.get(link) != null) { - loadedRepositories.get(link).clear(); - loadedRepositories.get(link).add(schems); - } else { - loadedRepositories.put(link, schems); - } + loadedRepositories.get(link, () -> new Seq<>(schems.size)).clear().add(schems); } unloadedRepositories.clear(); } diff --git a/core/src/mindustry/core/UI.java b/core/src/mindustry/core/UI.java index efa3894ced..1b0a805ce2 100644 --- a/core/src/mindustry/core/UI.java +++ b/core/src/mindustry/core/UI.java @@ -22,9 +22,7 @@ import arc.util.*; import mindustry.*; import mindustry.client.claj.*; -import mindustry.client.navigation.*; import mindustry.client.ui.*; -import mindustry.client.utils.*; import mindustry.editor.*; import mindustry.game.EventType.*; import mindustry.gen.*; @@ -33,7 +31,6 @@ import mindustry.ui.*; import mindustry.ui.dialogs.*; import mindustry.ui.fragments.*; -import mindustry.world.blocks.storage.*; import static arc.scene.actions.Actions.*; import static mindustry.Vars.*; @@ -422,7 +419,6 @@ public void showInfoPopup(String info, float duration, int align, int top, int l /** Shows a label in the world. This label is behind everything. Does not fade. */ public void showLabel(String info, float duration, float worldx, float worldy){ - if (Server.cn.b() && info.startsWith("Core #") && Vars.world.buildWorld(worldx, worldy) instanceof CoreBlock.CoreBuild) Navigation.navigator.map.put(Strings.parseInt(info.replace("Core #", "")), new Vec2(worldx, worldy)); var table = new Table(Styles.black3).margin(4); table.touchable = Touchable.disabled; table.update(() -> { diff --git a/core/src/mindustry/ui/Menus.java b/core/src/mindustry/ui/Menus.java index c651a84b98..ed5ee773d3 100644 --- a/core/src/mindustry/ui/Menus.java +++ b/core/src/mindustry/ui/Menus.java @@ -40,7 +40,7 @@ public static void menu(int menuId, String title, String message, String[][] opt options[0][0].contains("Downvote") && options[1][0].contains("Upvote") && Server.cn.b())) return; // phoenix network and cn are equally annoying if(title.contains("Basic Info and Rules") && Server.fish.b()) return; // fish is equally annoying (though this is a join popup, not a vote prompt) - Log.debug("Displaying menu " + menuId + " with title: " + title); + Log.debug("Displaying menu @ with title: @", menuId, title); ui.showMenu(title, message, options, (option) -> Call.menuChoose(player, menuId, option)); } @@ -48,7 +48,9 @@ public static void menu(int menuId, String title, String message, String[][] opt public static void followUpMenu(int menuId, String title, String message, String[][] options){ if(title == null) title = ""; if(options == null) options = new String[0][0]; + if(title.equals("Hello there") && Server.cn.b()) return; // Cn join popup + Log.debug("Displaying followup menu @ with title: @", menuId, title); ui.showFollowUpMenu(menuId, title, message, options, (option) -> Call.menuChoose(player, menuId, option)); } diff --git a/gradle.properties b/gradle.properties index 3617e93f3a..a82ade5e3c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,4 +28,4 @@ org.gradle.internal.http.connectionTimeout=100000 #kapt.verbose=true # For some reason kapt ir is just completely broken for us. I don't know why. kapt.use.jvm.ir=false -archash=6ba46537648804942be923d32e678d49602f010e +archash=cfe25db2731b9a8f605e8d09880fcd9bbb31acec