Skip to content

Commit

Permalink
- Automatically install and manage floodcompat.
Browse files Browse the repository at this point in the history
- Localize claj ping
  • Loading branch information
buthed010203 committed Oct 28, 2024
1 parent fc2f52f commit 00d9c7e
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 43 deletions.
2 changes: 2 additions & 0 deletions core/assets/bundles/bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ client.claj.portnotfound = CLaJ link missing port
client.claj.colonnotfound = CLaJ link missing colon
client.claj.wrongkeylength = CLaJ link is wrong length
client.claj.missingprefix = CLaJ link missing prefix
client.claj.ping.error = Error contacting server.
client.claj.ping.pending = Not yet contacted.

# Editor
client.editor.autosaved = Autosaved!
Expand Down
2 changes: 1 addition & 1 deletion core/src/mindustry/client/Commands.kt
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fun setupCommands() {
}
}

player.sendMessage("client.command.count.success"[type.localizedName, total, cap, free, freeFlagged, flagged, unflagged, players, command, logic, logicFlagged])
player.sendMessage("client.command.count.success".bundle(type.localizedName, total, cap, free, freeFlagged, flagged, unflagged, players, command, logic, logicFlagged))
}

// FINISHME: Add unit control/select command(s)
Expand Down
3 changes: 2 additions & 1 deletion core/src/mindustry/client/claj/ClajManagerDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import arc.scene.ui.layout.*
import arc.util.*
import mindustry.*
import mindustry.client.claj.ClajSupport.createRoom
import mindustry.client.utils.*
import mindustry.gen.*
import mindustry.ui.*
import mindustry.ui.dialogs.*
Expand Down Expand Up @@ -40,7 +41,7 @@ class ClajManagerDialog : BaseDialog("@client.claj.manage") {
Vars.ui.showErrorMessage(ignored.message)
}
}.disabled { list.children.size >= 4 }.padTop(8f).row()
rooms.label { if (Time.timeSinceMillis(lastPingAt) > 1000) { lastPingAt = Time.millis(); Vars.net.pingHost(serverIP, serverPort, { lastPing = it.ping }) { lastPing = -2 } }; "Ping: " + if (lastPing == -2) "[scarlet]Error contacting server." else if (lastPing == -1) "[orange]Not yet contacted." else "${lastPing}ms" }
rooms.label { if (Time.timeSinceMillis(lastPingAt) > 1000) { lastPingAt = Time.millis(); Vars.net.pingHost(serverIP, serverPort, { lastPing = it.ping }) { lastPing = -2 } }; if (lastPing == -2) "[scarlet]" + "client.claj.ping.error".bundle() else if (lastPing == -1) "[orange]" + "client.claj.ping.pending".bundle() else "ping".bundle(lastPing) }
}.height(550f).row()
val l = cont.labelWrap("@client.claj.info").labelAlign(2, 8).padTop(16f).width(400f).get()
l.style.fontColor = Color.lightGray
Expand Down
2 changes: 1 addition & 1 deletion core/src/mindustry/client/utils/ClientUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ fun <T> Iterable<T>.unescape(escapement: T, vararg escape: T): List<T> {
}

fun String.bundle(): String = Core.bundle[removePrefix("@")]
operator fun String.get(vararg args: Any?): String = Core.bundle.formatKt(removePrefix("@"), args)
fun String.bundle(vararg args: Any?): String = Core.bundle.formatKt(removePrefix("@"), args)

val X509Certificate.readableName: String
get() = subjectX500Principal.name.removePrefix("CN=")
Expand Down
78 changes: 53 additions & 25 deletions core/src/mindustry/client/utils/ServerUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package mindustry.client.utils

import arc.*
import arc.files.*
import arc.util.*
import mindustry.Vars.*
import mindustry.client.*
Expand Down Expand Up @@ -180,36 +181,63 @@ enum class CustomMode(
none,
flood {
val ioFloodCompatRepo = "mindustry-antigrief/FloodCompat"
var hasLoaded = false

override fun enable() {
super.enable()
flood.name
if (io() && net.client()) { // FINISHME: Load the mod at runtime
val floodcompatMod: Mods.LoadedMod? = mods.list().find { it.repo?.lowercase() == ioFloodCompatRepo.lowercase() }
Timer.schedule({
if (floodcompatMod === null) {
ui.chatfrag.addMsg(
"[accent]Floodcompat mod [scarlet]not[] found! Installing the [white]${ioFloodCompatRepo}[] mod is recommended for a better game experience." +
"\n[green]INSTALL")
.addButton("INSTALL") {
Toast(3f).add("Downloading mod")
ui.mods.githubImportMod(ioFloodCompatRepo, false, null, "") {
Toast(3f).add("Downloaded!")
val mod: Mods.LoadedMod = mods.list().find { it.repo?.lowercase() == ioFloodCompatRepo.lowercase() } ?: return@githubImportMod
mods.setEnabled(mod, true)
ui.chatfrag.addMsg("[accent]Mod downloaded! Restart game to apply mod.\n[red]RESTART").addButton("RESTART") { restartGame() };
}
}
} else if (!floodcompatMod.enabled()){
ui.chatfrag.addMsg(
"[accent]Floodcompat mod [scarlet]disabled[]! Enabling it is recommended for a better game experience." +
"\n[green]ENABLE (requires restart)")
.addButton("ENABLE (requires restart)") {
mods.setEnabled(floodcompatMod, true)
restartGame()
if (io() && net.client()) {
var floodMod: Mods.LoadedMod? = mods.getMod("floodcompat")

fun enable() { // Just enables the mod
if (hasLoaded) return // Only attempt to enable the mod once
hasLoaded = true

Log.warn("FloodCompat installed but disabled. Foo's will load it at runtime.")

mods.mods.remove(floodMod)
floodMod!!.dispose()
Core.settings.put("mod-floodcompat-enabled", true) // Has to be enabled for the mod to load
val mod = Reflect.invoke<Mods.LoadedMod>(mods, "loadMod", arrayOf(floodMod!!.file), Fi::class.java) // Load the mod and call the init() function
mod.main.init()
// Next 5 lines sort the new mod as if it were enabled without actually keeping it enabled after a restart
mod.state = Mods.ModState.enabled
mods.mods.add(mod)
Reflect.invoke<Void>(mods, "sortMods")
Reflect.set(mods, "lastOrderedMods", null) // Reset orderedMods cache
Core.settings.put("mod-floodcompat-enabled", false) // May as well disable it as it was before
}

fun download(update: Boolean = false) { // Downloads and enables the mod
Toast(3f).add(if (update) "Updating" else "Installing" + " FloodCompat")
Log.debug(if (update) "Updating" else "Installing" + " FloodCompat")
ui.mods.githubImportMod(ioFloodCompatRepo, true, null, floodMod?.meta?.version) {
val new = mods.mods.last { it.name == "floodcompat"} // newly downloaded flood compat if any
if (update && new != floodMod) { // Delete old flood mod for update. If new == old, there was no update.
floodMod!!.file.deleteDirectory()
floodMod!!.dispose()
mods.mods.remove(floodMod)
}
val reload = Reflect.get<Boolean>(mods, "requiresReload")
Reflect.set(mods, "requiresReload", reload)
Toast(3f).add("FloodCompat " + if (update) "updated" else "installed" + " successfully!")
Core.settings.put("mod-floodcompat-enabled", false) // Set as disabled as there's no reason to load it outside of flood gamemode
floodMod = mods.getMod("floodcompat") // floodMod is still null from before, set it to the mod we just downloaded
enable()
}
}, 2f)
}

if (floodMod === null) {
ui.showConfirm("[scarlet]FloodCompat mod not found!", "Installing the [accent]${ioFloodCompatRepo}[] mod is recommended for a better game experience. Would you like to install it?\nThis will not require a restart.") {
Toast(3f).add("Downloading mod")
download()
}
} else if (!floodMod.enabled()) {
if (!hasLoaded && Time.timeSinceMillis(Core.settings.getLong("lastfloodcompatupdate", Time.millis())) > 1000 * 60 * 30L) { // Update floodCompat every 30m
Core.settings.put("lastfloodcompatupdate", Time.millis())
(floodMod.root as? ZipFi)?.delete() // Close the current flood zip just in case its open somehow (it should not be)
download(true)
} else enable() // Enable the mod as normal otherwise
}
}
}
},
Expand Down
13 changes: 7 additions & 6 deletions core/src/mindustry/mod/Mods.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,16 +222,16 @@ public void loadIcons(long start, int limit) { // FINISHME: This could be furthe
}

private void loadIcon(LoadedMod mod){
mod.attemptedIconLoad = true;
//try to load icon for each mod that can have one
if(mod.root.child("icon.png").exists() && !headless){
if(mod.root.child("icon.png").exists() && !headless && !mod.attemptedIconLoad){
try{
mod.iconTexture = new Texture(mod.root.child("icon.png"));
mod.iconTexture.setFilter(TextureFilter.linear);
}catch(Throwable t){
Log.err("Failed to load icon for mod '" + mod.name + "'.", t);
}
}
mod.attemptedIconLoad = true;
}

private int packSprites(Seq<Fi> sprites, LoadedMod mod, boolean prefix, LinkedBlockingQueue<Pair<String, Pixmap>>[] queues){
Expand Down Expand Up @@ -1133,8 +1133,8 @@ public static class LoadedMod implements Publishable, Disposable{

/** Foo's addition to track whether we have tried to load this mod's icon */
public boolean attemptedIconLoad;
private static boolean iconLoadingOptimization = Core.settings.getBool("modiconloadingoptimization");
private static ObjectSet<String> iconDeferralUnsupported = ObjectSet.with("mi2-utilities-java", "olupis");
private static final boolean iconLoadingOptimization = Core.settings.getBool("modiconloadingoptimization");
private static final ObjectSet<String> iconDeferralUnsupported = ObjectSet.with("mi2-utilities-java", "olupis");

public LoadedMod(Fi file, Fi root, Mod main, ClassLoader loader, ModMeta meta){
this.root = root;
Expand All @@ -1143,9 +1143,9 @@ public LoadedMod(Fi file, Fi root, Mod main, ClassLoader loader, ModMeta meta){
this.main = main;
this.meta = meta;
this.name = meta.name.toLowerCase(Locale.ROOT).replace(" ", "-");
if(shouldBeEnabled() && (!iconLoadingOptimization || iconDeferralUnsupported.contains(this.name))){ // This is terrible.
if(shouldBeEnabled() && (!iconLoadingOptimization || iconDeferralUnsupported.contains(this.name))){ // This is terrible. Loads icons immediately if icon loading optimization is disabled
Core.app.post(() -> Vars.mods.loadIcon(this));
}
}else Vars.mods.hasLoadedIcons = false; // Makes sure that another round of icon loading will happen if icon loading optimization is enabled and we're adding a new mod
}

/** @return whether this is a java class mod. */
Expand Down Expand Up @@ -1210,6 +1210,7 @@ public int getMinMajor(){

@Override
public void dispose(){
attemptedIconLoad = true; // Prevents attempts at loading it again
if(iconTexture != null){
iconTexture.dispose();
iconTexture = null;
Expand Down
13 changes: 5 additions & 8 deletions core/src/mindustry/ui/dialogs/ModsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,16 @@ public class ModsDialog extends BaseDialog{
private boolean autoUpdating; // Whether mods are currently being auto updated
private float scroll = 0f;

private Runnable autoUpdaterHandler = () -> { // RUN THIS ON THE MAIN THREAD
private final Runnable autoUpdaterHandler = () -> { // RUN THIS ON THE MAIN THREAD
if (++prompted == expected) { // FINISHME: Awful
autoUpdating = false;
if (mods.requiresReload()){
if (Core.settings.getInt("modautoupdate") == 2) {
ClientUtils.restartGame();
reload();
}
if (Core.settings.getInt("modautoupdate") == 2) reload();
new Toast(5f).add("[accent]Mod updates found, they will be installed after restart.");
} else new Toast(5f).add("[accent]No mod updates found.");
}
};
private ObjectMap<String, Runnable> onSuccess = new ObjectMap();
private final ObjectMap<String, Runnable> onSuccess = new ObjectMap<>();

public ModsDialog(){
super("@mods");
Expand Down Expand Up @@ -126,7 +123,7 @@ public ModsDialog(){
autoUpdating = true;
Log.debug("Checking for mod updates @", Time.timeSinceMillis(settings.getLong("lastmodupdate", hour + 1)) / (60*1000f));
Core.settings.put("lastmodupdate", Time.millis());
for (Mods.LoadedMod mod : mods.mods.copy().shuffle()) { // Use shuffled mod list, if the user has more than 30 active mods, this will ensure that each is checked at least somewhat frequently
for (Mods.LoadedMod mod : mods.mods.copy().shuffle()) { // Use shuffled mod list, if the user has more than 30 active mods, this will ensure that each is checked at least somewhat frequently FINISHME: This should take dependencies and requirements into account which we don't do currently
if (!mod.enabled() || mod.getRepo() == null || !settings.getBool(mod.autoUpdateString(), true)) continue;
if (expected++ >= 30) continue; // Only make up to 30 api requests
onSuccess.put(mod.getRepo(), autoUpdaterHandler);
Expand Down Expand Up @@ -711,7 +708,7 @@ private void handleMod(String repo, HttpResponse result, @Nullable String prevVe
private void importSuccess(String repo){
var func = onSuccess.remove(repo);
if(func == null) return;
Core.app.post(() -> func.run());
Core.app.post(func);
}

private void importFail(Throwable t){
Expand Down
2 changes: 1 addition & 1 deletion core/src/mindustry/ui/fragments/ChatFragment.java
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ private void format(boolean moveButtons) {
if(sender == null){ //no sender, this is a server message?
formattedMessage = prefix + (message == null ? "" : message);
} else {
if (Server.darkdustry.b()) formattedMessage = prefix + message; // Hack to allow darkdustry translation as they don't change sender
if (Server.darkdustry.b()) formattedMessage = prefix + message; // Hack to allow darkdustry translation as they don't change sender FINISHME: We need to rework this system badly as some servers change the messages significantly
else formattedMessage = prefix + "[coral][[[white]" + sender + "[coral]]:[white] " + unformatted;
}
int shift = formattedMessage.length() - initial;
Expand Down

0 comments on commit 00d9c7e

Please sign in to comment.