mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-11 16:36:58 -04:00
improve performance, bug fixes, add start dialog progress, load much more async, relative time log
This commit is contained in:
parent
6e27430994
commit
2203e44d05
@ -1,7 +1,7 @@
|
||||
# Modding
|
||||
|
||||
## mod.json
|
||||
In your jar file (the mod) must be a file called `mod.json`.
|
||||
In the root folder of your jar file (the mod) must be a file called `mod.json`. It contains metadata for the mod loader.
|
||||
### Example
|
||||
```json
|
||||
{
|
||||
|
@ -13,9 +13,34 @@
|
||||
|
||||
package de.bixilon.minosoft;
|
||||
|
||||
import de.bixilon.minosoft.util.OSUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Config {
|
||||
public static final String configFileName = "config.json";
|
||||
public static final boolean skipAuthentication = false; // only for offline development
|
||||
public static final boolean colorLog = true;
|
||||
public static final String configFileName = "config.json"; // Path of the minosoft base configuration (located in AppData/Minosoft/config)
|
||||
public static final boolean skipAuthentication = false; // disables all connections to mojang
|
||||
public static final boolean colorLog = true; // the log should be colored with ANSI (does not affect chat components)
|
||||
public static final boolean logRelativeTime = false; // prefix all log messages with the relative start time in milliseconds instead of the formatted time
|
||||
|
||||
public static String homeDir;
|
||||
|
||||
static {
|
||||
// Sets Config.homeDir to the correct folder per OS
|
||||
homeDir = System.getProperty("user.home");
|
||||
if (!homeDir.endsWith(File.separator)) {
|
||||
homeDir += "/";
|
||||
}
|
||||
homeDir += switch (OSUtil.getOS()) {
|
||||
case LINUX -> ".local/share/minosoft/";
|
||||
case WINDOWS -> "AppData/Roaming/Minosoft/";
|
||||
case MAC -> "Library/Application Support/Minosoft/";
|
||||
case OTHER -> ".minosoft/";
|
||||
};
|
||||
File folder = new File(homeDir);
|
||||
if (!folder.exists() && !folder.mkdirs()) {
|
||||
// failed creating folder
|
||||
throw new RuntimeException(String.format("Could not create home folder (%s)!", homeDir));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,16 @@ import de.bixilon.minosoft.gui.main.Server;
|
||||
import de.bixilon.minosoft.gui.main.ServerListCell;
|
||||
import de.bixilon.minosoft.logging.Log;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.ProgressBar;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Callback;
|
||||
@ -33,14 +37,32 @@ import javafx.util.Callback;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Launcher extends Application {
|
||||
private static ProgressBar progressBar;
|
||||
private static Dialog<Boolean> progressDialog;
|
||||
|
||||
public static void start() {
|
||||
Log.info("Starting launcher...");
|
||||
launch();
|
||||
Log.info("Launcher started!");
|
||||
}
|
||||
|
||||
protected static void setProgressBar(int jobsLeft) {
|
||||
Platform.runLater(() -> {
|
||||
if (progressBar == null || progressDialog == null) {
|
||||
return;
|
||||
}
|
||||
if (jobsLeft == 0) {
|
||||
progressDialog.setResult(Boolean.TRUE);
|
||||
progressDialog.close();
|
||||
return;
|
||||
}
|
||||
progressBar.setProgress(1.0F / jobsLeft);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws IOException {
|
||||
Log.info("Starting launcher...");
|
||||
Log.info("Preparing main window...");
|
||||
|
||||
GUITools.versionList.setCellFactory(new Callback<>() {
|
||||
@Override
|
||||
@ -74,6 +96,18 @@ public class Launcher extends Application {
|
||||
if (Minosoft.getSelectedAccount() == null) {
|
||||
MainWindow.manageAccounts();
|
||||
}
|
||||
Log.info("Launcher started!");
|
||||
Log.info("Main window prepared!");
|
||||
if (Minosoft.getStartUpJobsLeft() == 0) {
|
||||
return;
|
||||
}
|
||||
progressDialog = new Dialog<>();
|
||||
progressDialog.setHeaderText("Minosoft is still starting up...");
|
||||
progressDialog.setTitle("Starting up");
|
||||
GridPane grid = new GridPane();
|
||||
progressBar = new ProgressBar();
|
||||
progressBar.setProgress(1.0D / 5);
|
||||
grid.add(progressBar, 0, 0);
|
||||
progressDialog.getDialogPane().setContent(grid);
|
||||
progressDialog.show();
|
||||
}
|
||||
}
|
@ -18,34 +18,35 @@ import de.bixilon.minosoft.config.Configuration;
|
||||
import de.bixilon.minosoft.config.GameConfiguration;
|
||||
import de.bixilon.minosoft.game.datatypes.objectLoader.versions.Versions;
|
||||
import de.bixilon.minosoft.gui.main.AccountListCell;
|
||||
import de.bixilon.minosoft.gui.main.MainWindow;
|
||||
import de.bixilon.minosoft.gui.main.Server;
|
||||
import de.bixilon.minosoft.logging.Log;
|
||||
import de.bixilon.minosoft.logging.LogLevels;
|
||||
import de.bixilon.minosoft.modding.event.EventManager;
|
||||
import de.bixilon.minosoft.modding.loading.ModLoader;
|
||||
import de.bixilon.minosoft.util.OSUtil;
|
||||
import de.bixilon.minosoft.util.Util;
|
||||
import de.bixilon.minosoft.util.mojang.api.MojangAccount;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class Minosoft {
|
||||
public static final HashSet<EventManager> eventManagers = new HashSet<>();
|
||||
private static final CountDownLatch startStatus = new CountDownLatch(2); // number of critical components (wait for them before other "big" actions)
|
||||
public static HashBiMap<String, MojangAccount> accountList;
|
||||
public static MojangAccount selectedAccount;
|
||||
public static ArrayList<Server> serverList;
|
||||
public static final HashSet<EventManager> eventManagers = new HashSet<>();
|
||||
static Configuration config;
|
||||
public static Configuration config;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// init log thread
|
||||
Log.initThread();
|
||||
|
||||
Log.info("Starting...");
|
||||
setConfigFolder();
|
||||
Log.info("Reading config file...");
|
||||
try {
|
||||
config = new Configuration(Config.configFileName);
|
||||
@ -58,52 +59,49 @@ public class Minosoft {
|
||||
// set log level from config
|
||||
Log.setLevel(LogLevels.valueOf(config.getString(GameConfiguration.GENERAL_LOG_LEVEL)));
|
||||
Log.info(String.format("Logging info with level: %s", Log.getLevel()));
|
||||
Log.info("Loading versions.json...");
|
||||
long mappingStartLoadingTime = System.currentTimeMillis();
|
||||
try {
|
||||
Versions.load(Util.readJsonAsset("mapping/versions.json"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
Log.info(String.format("Loaded versions mapping in %dms", (System.currentTimeMillis() - mappingStartLoadingTime)));
|
||||
|
||||
Log.debug("Refreshing client token...");
|
||||
checkClientToken();
|
||||
accountList = config.getMojangAccounts();
|
||||
selectAccount(accountList.get(config.getString(GameConfiguration.ACCOUNT_SELECTED)));
|
||||
|
||||
serverList = config.getServers();
|
||||
new Thread(() -> {
|
||||
ArrayList<Callable<Boolean>> startCallables = new ArrayList<>();
|
||||
startCallables.add(() -> {
|
||||
Log.info("Loading versions.json...");
|
||||
long mappingStartLoadingTime = System.currentTimeMillis();
|
||||
try {
|
||||
ModLoader.loadMods();
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace();
|
||||
Versions.load(Util.readJsonAsset("mapping/versions.json"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}, "ModLoader").start();
|
||||
Launcher.start();
|
||||
Log.info(String.format("Loaded versions mapping in %dms", (System.currentTimeMillis() - mappingStartLoadingTime)));
|
||||
countDownStart(); // (another) critical component was loaded
|
||||
return true;
|
||||
});
|
||||
startCallables.add(() -> {
|
||||
Log.debug("Refreshing client token...");
|
||||
checkClientToken();
|
||||
accountList = config.getMojangAccounts();
|
||||
selectAccount(accountList.get(config.getString(GameConfiguration.ACCOUNT_SELECTED)));
|
||||
return true;
|
||||
});
|
||||
startCallables.add(() -> {
|
||||
ModLoader.loadMods();
|
||||
countDownStart(); // (another) critical component was loaded
|
||||
return true;
|
||||
});
|
||||
|
||||
startCallables.add(() -> {
|
||||
Launcher.start();
|
||||
return true;
|
||||
});
|
||||
try {
|
||||
Util.executeInThreadPool("Start", startCallables);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Config.homeDir to the correct folder per OS
|
||||
*/
|
||||
public static void setConfigFolder() {
|
||||
String path = System.getProperty("user.home");
|
||||
if (!path.endsWith(File.separator)) {
|
||||
path += "/";
|
||||
}
|
||||
path += switch (OSUtil.getOS()) {
|
||||
case LINUX -> ".local/share/minosoft/";
|
||||
case WINDOWS -> "AppData/Roaming/Minosoft/";
|
||||
case MAC -> "Library/Application Support/Minosoft/";
|
||||
case OTHER -> ".minosoft/";
|
||||
};
|
||||
File folder = new File(path);
|
||||
if (!folder.exists() && !folder.mkdirs()) {
|
||||
// failed creating folder
|
||||
throw new RuntimeException(String.format("Could not create home folder (%s)!", path));
|
||||
}
|
||||
Config.homeDir = path;
|
||||
private static void countDownStart() {
|
||||
startStatus.countDown();
|
||||
Launcher.setProgressBar((int) startStatus.getCount());
|
||||
}
|
||||
|
||||
public static void checkClientToken() {
|
||||
@ -130,6 +128,9 @@ public class Minosoft {
|
||||
}
|
||||
config.putString(GameConfiguration.ACCOUNT_SELECTED, account.getUserId());
|
||||
selectedAccount = account;
|
||||
if (MainWindow.accountMenu2 != null) {
|
||||
MainWindow.accountMenu2.setText(String.format("Account (%s)", account.getPlayerName()));
|
||||
}
|
||||
account.saveToConfig();
|
||||
}
|
||||
|
||||
@ -148,4 +149,19 @@ public class Minosoft {
|
||||
public static MojangAccount getSelectedAccount() {
|
||||
return selectedAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until all critical components are started
|
||||
*/
|
||||
public static void waitForStartup() {
|
||||
try {
|
||||
startStatus.await();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getStartUpJobsLeft() {
|
||||
return (int) startStatus.getCount();
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,12 @@ public class MainWindow implements Initializable {
|
||||
@FXML
|
||||
public Menu accountMenu;
|
||||
|
||||
public static Menu accountMenu2;
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
serversPane.setCenter(ServerListCell.listView);
|
||||
accountMenu2 = accountMenu;
|
||||
if (Minosoft.getSelectedAccount() != null) {
|
||||
accountMenu.setText(String.format("Account (%s)", Minosoft.getSelectedAccount().getPlayerName()));
|
||||
}
|
||||
|
@ -41,7 +41,6 @@ import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextFlow;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.util.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
@ -349,7 +348,7 @@ public class ServerListCell extends ListCell<Server> implements Initializable {
|
||||
|
||||
public void showInfo() {
|
||||
|
||||
Dialog<Pair<String, String>> dialog = new Dialog<>();
|
||||
Dialog<?> dialog = new Dialog<>();
|
||||
dialog.setTitle("View server info: " + server.getName());
|
||||
|
||||
ButtonType loginButtonType = ButtonType.CLOSE;
|
||||
|
@ -24,6 +24,7 @@ import java.util.concurrent.LinkedBlockingQueue;
|
||||
public class Log {
|
||||
final static SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
final static LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
|
||||
final static long startTime = System.currentTimeMillis();
|
||||
static LogLevels level = LogLevels.PROTOCOL;
|
||||
|
||||
public static void initThread() {
|
||||
@ -64,7 +65,11 @@ public class Log {
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
builder.append(timeFormat.format(System.currentTimeMillis()));
|
||||
if (Config.logRelativeTime) {
|
||||
builder.append(System.currentTimeMillis() - startTime);
|
||||
} else {
|
||||
builder.append(timeFormat.format(System.currentTimeMillis()));
|
||||
}
|
||||
builder.append("] [");
|
||||
builder.append(Thread.currentThread().getName());
|
||||
builder.append("] [");
|
||||
|
@ -32,6 +32,7 @@ public class ModLoader {
|
||||
static final LinkedList<MinosoftMod> mods = new LinkedList<>();
|
||||
|
||||
public static void loadMods() throws Exception {
|
||||
Log.verbose("Start loading mods...");
|
||||
// load all jars, parse the mod.json
|
||||
// sort the list and prioritize
|
||||
// load all lists and dependencies async
|
||||
@ -85,6 +86,7 @@ public class ModLoader {
|
||||
Log.warn(String.format("An error occurred while loading %s", instance.getInfo()));
|
||||
}
|
||||
});
|
||||
Log.verbose("Loading all mods finished!");
|
||||
}
|
||||
|
||||
public static MinosoftMod loadMod(File file) {
|
||||
|
@ -22,10 +22,11 @@ import de.bixilon.minosoft.protocol.protocol.OutByteBuffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class PluginChannelHandler {
|
||||
final HashMap<String, ArrayList<ChannelHandler>> channels = new HashMap<>();
|
||||
final HashMap<String, ArrayList<LoginChannelHandler>> loginChannels = new HashMap<>();
|
||||
final HashMap<String, HashSet<ChannelHandler>> channels = new HashMap<>();
|
||||
final HashMap<String, HashSet<LoginChannelHandler>> loginChannels = new HashMap<>();
|
||||
final ArrayList<String> registeredClientChannels = new ArrayList<>();
|
||||
final ArrayList<String> registeredServerChannels = new ArrayList<>();
|
||||
final Connection connection;
|
||||
@ -37,7 +38,7 @@ public class PluginChannelHandler {
|
||||
public void registerClientHandler(String name, ChannelHandler handler) {
|
||||
if (channels.get(name) == null) {
|
||||
// no channel with that name was registered yet
|
||||
ArrayList<ChannelHandler> handlerList = new ArrayList<>();
|
||||
HashSet<ChannelHandler> handlerList = new HashSet<>();
|
||||
handlerList.add(handler);
|
||||
channels.put(name, handlerList);
|
||||
return;
|
||||
@ -50,7 +51,7 @@ public class PluginChannelHandler {
|
||||
public void registerLoginClientHandler(String name, LoginChannelHandler handler) {
|
||||
if (loginChannels.get(name) == null) {
|
||||
// no channel with that name was registered yet
|
||||
ArrayList<LoginChannelHandler> handlerList = new ArrayList<>();
|
||||
HashSet<LoginChannelHandler> handlerList = new HashSet<>();
|
||||
handlerList.add(handler);
|
||||
loginChannels.put(name, handlerList);
|
||||
return;
|
||||
|
@ -92,6 +92,7 @@ public class Connection {
|
||||
this.desiredVersionNumber = protocolId;
|
||||
|
||||
Thread resolveThread = new Thread(() -> {
|
||||
Minosoft.waitForStartup(); // wait until mappings are loaded
|
||||
if (desiredVersionNumber != -1) {
|
||||
setVersion(Versions.getVersionById(desiredVersionNumber));
|
||||
}
|
||||
@ -212,7 +213,7 @@ public class Connection {
|
||||
return false;
|
||||
}
|
||||
|
||||
void startHandlingThread() {
|
||||
private void startHandlingThread() {
|
||||
handleThread = new Thread(() -> {
|
||||
while (isConnected()) {
|
||||
ClientboundPacket packet;
|
||||
@ -242,7 +243,7 @@ public class Connection {
|
||||
return pluginChannelHandler;
|
||||
}
|
||||
|
||||
public void registerDefaultChannels() {
|
||||
private void registerDefaultChannels() {
|
||||
// MC|Brand
|
||||
getPluginChannelHandler().registerClientHandler(DefaultPluginChannels.MC_BRAND.getChangeableIdentifier().get(version.getProtocolVersion()), (handler, buffer) -> {
|
||||
String serverVersion;
|
||||
|
@ -26,8 +26,8 @@ import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
@ -202,7 +202,7 @@ public final class Util {
|
||||
fileOutputStream.close();
|
||||
}
|
||||
|
||||
public static <T> void executeInThreadPool(String name, HashSet<Callable<T>> callables) throws InterruptedException {
|
||||
public static <T> void executeInThreadPool(String name, Collection<Callable<T>> callables) throws InterruptedException {
|
||||
ExecutorService phaseLoader = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), getThreadFactory(name));
|
||||
phaseLoader.invokeAll(callables);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user