I am writing a simple console program for Minecraft in javaFX. Sometimes it works fine but sometimes when the console is updating ill randomly get spammed with this error:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException: Cannot read the array length because "this.runs" is null
at javafx.graphics@18.0.1/com.sun.javafx.text.PrismTextLayout.addTextRun(PrismTextLayout.java:770)
at javafx.graphics@18.0.1/com.sun.javafx.text.GlyphLayout.addTextRun(GlyphLayout.java:140)
at javafx.graphics@18.0.1/com.sun.javafx.text.GlyphLayout.breakRuns(GlyphLayout.java:210)
at javafx.graphics@18.0.1/com.sun.javafx.text.PrismTextLayout.buildRuns(PrismTextLayout.java:785)
at javafx.graphics@18.0.1/com.sun.javafx.text.PrismTextLayout.layout(PrismTextLayout.java:1036)
at javafx.graphics@18.0.1/com.sun.javafx.text.PrismTextLayout.ensureLayout(PrismTextLayout.java:223)
at javafx.graphics@18.0.1/com.sun.javafx.text.PrismTextLayout.getBounds(PrismTextLayout.java:246)
at javafx.graphics@18.0.1/javafx.scene.text.Text.getLogicalBounds(Text.java:432)
at javafx.graphics@18.0.1/javafx.scene.text.Text.doComputeGeomBounds(Text.java:1187)
at javafx.graphics@18.0.1/javafx.scene.text.Text$1.doComputeGeomBounds(Text.java:149)
at javafx.graphics@18.0.1/com.sun.javafx.scene.shape.TextHelper.computeGeomBoundsImpl(TextHelper.java:90)
at javafx.graphics@18.0.1/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:116)
at javafx.graphics@18.0.1/javafx.scene.Node.updateGeomBounds(Node.java:3818)
at javafx.graphics@18.0.1/javafx.scene.Node.getGeomBounds(Node.java:3780)
at javafx.graphics@18.0.1/javafx.scene.Node.getLocalBounds(Node.java:3728)
at javafx.graphics@18.0.1/javafx.scene.Node$MiscProperties$3.computeBounds(Node.java:6818)
at javafx.graphics@18.0.1/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9712)
at javafx.graphics@18.0.1/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9682)
at javafx.graphics@18.0.1/javafx.scene.Node.getBoundsInLocal(Node.java:3408)
at javafx.controls@18.0.1/javafx.scene.control.skin.TextAreaSkin$ContentView.layoutChildren(TextAreaSkin.java:1324)
at javafx.graphics@18.0.1/javafx.scene.Parent.layout(Parent.java:1207)
at javafx.graphics@18.0.1/javafx.scene.Parent.layout(Parent.java:1214)
at javafx.graphics@18.0.1/javafx.scene.Parent.layout(Parent.java:1214)
at javafx.graphics@18.0.1/javafx.scene.Parent.layout(Parent.java:1214)
at javafx.graphics@18.0.1/javafx.scene.Parent.layout(Parent.java:1214)
at javafx.graphics@18.0.1/javafx.scene.Parent.layout(Parent.java:1214)
at javafx.graphics@18.0.1/javafx.scene.Scene.doLayoutPass(Scene.java:579)
at javafx.graphics@18.0.1/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2499)
at javafx.graphics@18.0.1/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:405)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics@18.0.1/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:404)
at javafx.graphics@18.0.1/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:434)
at javafx.graphics@18.0.1/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:575)
at javafx.graphics@18.0.1/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:555)
at javafx.graphics@18.0.1/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:548)
at javafx.graphics@18.0.1/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:352)
at javafx.graphics@18.0.1/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics@18.0.1/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics@18.0.1/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)
at java.base/java.lang.Thread.run(Thread.java:833)
The error message is not even remotely helpful and I cant figure out whats triggering the error. If anyone knows what triggers this error please let me know so I can fix it. Thanks.
Edit: Heres my source code
package com.koolade446.mconsole;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventType;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.*;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;
import org.jetbrains.annotations.Nullable;
import org.json.JSONObject;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public class MainController {
@FXML
public ComboBox<String> typeBox;
@FXML
public ComboBox<String> versionBox;
@FXML
public TextArea console;
@FXML
public Button startStopButton;
@FXML
public Button killButton;
@FXML
Button changeVersionButton;
@FXML
Label pathContainer;
@FXML
TextField commandBox;
private final Map<String, List<String>> typeMap = new HashMap<>();
private boolean serverIsStarted = false;
private ExecutorService executorService;
private Path directoryPath = Path.of(new File(".").getCanonicalPath());
private final String[] endpoints = new String[]{
"servers/spigot",
"vanilla/vanilla",
"vanilla/snapshot",
"modded/forge",
"modded/fabric"
};
File serverJar = new File(directoryPath + "/server.jar");
private PrintWriter outputStreamWriter = null;
public MainController() throws IOException {
}
public void initialize() throws IOException, URISyntaxException {
startStopButton.setStyle("-fx-background-image: url('power.png')");
killButton.setStyle("-fx-background-image: url('kill.png')");
pathContainer.setText(directoryPath.toString());
Platform.runLater(() -> {
try {
for (String endpoint : endpoints) {
List<String> l = new ArrayList<>();
URL url = new URL("https://serverjars.com/api/fetchAll/" + endpoint);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.connect();
JSONObject responseJson = new JSONObject(new BufferedReader(new InputStreamReader(con.getInputStream())).lines().collect(Collectors.joining()));
con.disconnect();
for (Object obj : responseJson.getJSONArray("response")) {
JSONObject jsonObject = (JSONObject) obj;
l.add(jsonObject.getString("version"));
}
typeMap.put(endpoint.split("/")[1].replace("/", ""), l);
}
typeBox.getItems().addAll(typeMap.keySet());
} catch (Exception e) {
e.printStackTrace();
}
});
}
public void changeVersionOptions() {
versionBox.getItems().clear();
versionBox.setValue(null);
versionBox.getItems().addAll(typeMap.get(typeBox.getValue()));
}
public void updateVersion(ActionEvent actionEvent) throws MalformedURLException {
printToConsole("INFO", "Attempting to update to " + typeBox.getValue() + " " + versionBox.getValue());
Platform.runLater(() -> {
try {
URL url = new URL("https://serverjars.com/api/fetchJar/" + endpoints[getClosestIndex(endpoints, typeBox.getValue())] + "/" + versionBox.getValue());
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
printToConsole("INFO", "Connecting to servers...");
con.connect();
printToConsole("INFO", "Downloading new jar file...");
InputStream is = con.getInputStream();
FileOutputStream fos = new FileOutputStream(serverJar);
printToConsole("INFO", "Writing new jar file...");
byte[] jarFileData = is.readAllBytes();
System.out.println(serverJar.getPath());
fos.write(jarFileData, 0, jarFileData.length);
is.close();
fos.close();
con.disconnect();
printToConsole("INFO", "Version successfully updated!");
} catch (Exception e) {
printToConsole("ERROR", "Error when updating version. Please make sure you select a type and version");
e.printStackTrace();
}
});
}
public void changeFolder(ActionEvent event) throws IOException {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Select minecraft server folder");
File file = directoryChooser.showDialog(null);
directoryPath = Path.of(file.getCanonicalPath());
pathContainer.setText(directoryPath.toString());
serverJar = new File(directoryPath + "/server.jar");
}
private int getClosestIndex(String[] array, String option) throws Exception {
int index = -1;
for (String str : array) {
index++;
if (str.contains(option)) return index;
}
return 0;
}
private void printToConsole(String sender, String message) {
console.appendText(String.format(
"[%s] %s\n",
sender,
message
));
}
public void togglePowerState(ActionEvent actionEvent) {
if (executorService == null || executorService.isTerminated()) {
//This is the bit that I think is causing it but idk how or how to fix it
Task<Boolean> runnable = new Task() {
@Override
protected Boolean call() {
try {
Process process;
ProcessBuilder pb = new ProcessBuilder("java", "-jar", "-Xmx5G", "-Xms1G", "server.jar", "nogui");
pb.directory(new File(directoryPath.toString()));
process = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
setOutputStreamWriter(new PrintWriter(process.getOutputStream()));
String line;
while (process.isAlive()) {
if ((line = br.readLine()) != null) {
printToConsole("MINECRAFT", line);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return true;
}
};
executorService = Executors.newFixedThreadPool(1);
executorService.execute(runnable);
executorService.shutdown();
} else if (!executorService.isTerminated()) {
outputStreamWriter.println("stop");
outputStreamWriter.flush();
}
}
public void killServer(ActionEvent actionEvent) {
if (!executorService.isTerminated()) executorService.shutdownNow();
}
public void sendCommand(ActionEvent event) throws IOException {
outputStreamWriter.println(commandBox.getText());
outputStreamWriter.flush();
commandBox.setText("");
}
public void onKeyPress(KeyEvent keyEvent) throws IOException {
if (keyEvent.getCode().equals(KeyCode.ENTER)) {
outputStreamWriter.println(commandBox.getText());
outputStreamWriter.flush();
commandBox.setText("");
}
}
public void setOutputStreamWriter(PrintWriter outputStreamWriter) {
this.outputStreamWriter = outputStreamWriter;
}
}