forked from GeyserMC/Geyser
Add GUI to standalone
This commit is contained in:
parent
699ae0b88e
commit
61072948b9
10 changed files with 807 additions and 10 deletions
|
@ -20,7 +20,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.minecrell</groupId>
|
<groupId>net.minecrell</groupId>
|
||||||
<artifactId>terminalconsoleappender</artifactId>
|
<artifactId>terminalconsoleappender</artifactId>
|
||||||
<version>1.1.1</version>
|
<version>1.2.0</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
|
|
|
@ -25,16 +25,23 @@
|
||||||
|
|
||||||
package org.geysermc.platform.standalone;
|
package org.geysermc.platform.standalone;
|
||||||
|
|
||||||
import org.geysermc.connector.common.PlatformType;
|
import lombok.Getter;
|
||||||
|
import net.minecrell.terminalconsole.TerminalConsoleAppender;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.core.Appender;
|
||||||
|
import org.apache.logging.log4j.core.Logger;
|
||||||
|
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.common.PlatformType;
|
||||||
import org.geysermc.connector.command.CommandManager;
|
import org.geysermc.connector.command.CommandManager;
|
||||||
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.connector.dump.BootstrapDumpInfo;
|
import org.geysermc.connector.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
|
||||||
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
|
||||||
|
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.connector.utils.FileUtils;
|
import org.geysermc.connector.utils.FileUtils;
|
||||||
import org.geysermc.platform.standalone.command.GeyserCommandManager;
|
import org.geysermc.platform.standalone.command.GeyserCommandManager;
|
||||||
|
import org.geysermc.platform.standalone.gui.GeyserStandaloneGUI;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -49,14 +56,49 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
private GeyserStandaloneLogger geyserLogger;
|
private GeyserStandaloneLogger geyserLogger;
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||||
|
|
||||||
|
private GeyserStandaloneGUI gui;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private boolean useGui = System.console() == null;
|
||||||
|
|
||||||
private GeyserConnector connector;
|
private GeyserConnector connector;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
for (String arg : args) {
|
||||||
|
// By default, standalone Geyser will check if it should open the GUI based on if the GUI is null
|
||||||
|
// Optionally, you can force the use of a GUI or no GUI by specifying args
|
||||||
|
if (arg.equals("gui")) {
|
||||||
|
new GeyserStandaloneBootstrap().onEnable(true);
|
||||||
|
return;
|
||||||
|
} else if (arg.equals("nogui")) {
|
||||||
|
new GeyserStandaloneBootstrap().onEnable(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
new GeyserStandaloneBootstrap().onEnable();
|
new GeyserStandaloneBootstrap().onEnable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onEnable(boolean useGui) {
|
||||||
|
this.useGui = useGui;
|
||||||
|
this.onEnable();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
Logger logger = (Logger) LogManager.getRootLogger();
|
||||||
|
for (Appender appender : logger.getAppenders().values()) {
|
||||||
|
// Remove the appender that is not in use
|
||||||
|
// Prevents multiple appenders/double logging and removes harmless errors
|
||||||
|
if ((useGui && appender instanceof TerminalConsoleAppender) || (!useGui && appender instanceof ConsoleAppender)) {
|
||||||
|
logger.removeAppender(appender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (useGui && gui == null) {
|
||||||
|
gui = new GeyserStandaloneGUI();
|
||||||
|
gui.redirectSystemStreams();
|
||||||
|
gui.startUpdateThread();
|
||||||
|
}
|
||||||
|
|
||||||
geyserLogger = new GeyserStandaloneLogger();
|
geyserLogger = new GeyserStandaloneLogger();
|
||||||
|
|
||||||
LoopbackUtil.checkLoopback(geyserLogger);
|
LoopbackUtil.checkLoopback(geyserLogger);
|
||||||
|
@ -73,9 +115,15 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
connector = GeyserConnector.start(PlatformType.STANDALONE, this);
|
connector = GeyserConnector.start(PlatformType.STANDALONE, this);
|
||||||
geyserCommandManager = new GeyserCommandManager(connector);
|
geyserCommandManager = new GeyserCommandManager(connector);
|
||||||
|
|
||||||
|
if (gui != null) {
|
||||||
|
gui.setupInterface(geyserLogger, geyserCommandManager);
|
||||||
|
}
|
||||||
|
|
||||||
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
|
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
|
||||||
|
|
||||||
geyserLogger.start();
|
if (!useGui) {
|
||||||
|
geyserLogger.start(); // Throws an error otherwise
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -30,6 +30,7 @@ import lombok.extern.log4j.Log4j2;
|
||||||
|
|
||||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.Level;
|
||||||
import org.apache.logging.log4j.core.config.Configurator;
|
import org.apache.logging.log4j.core.config.Configurator;
|
||||||
import org.geysermc.connector.common.ChatColor;
|
import org.geysermc.connector.common.ChatColor;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
@ -96,7 +97,15 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements org
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDebug(boolean debug) {
|
public void setDebug(boolean debug) {
|
||||||
Configurator.setLevel(log.getName(), debug ? org.apache.logging.log4j.Level.DEBUG : log.getLevel());
|
Configurator.setLevel(log.getName(), debug ? Level.DEBUG : Level.INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for setting debug mode in GUI mode
|
||||||
|
* @return if debug is enabled
|
||||||
|
*/
|
||||||
|
public boolean isDebug() {
|
||||||
|
return log.isDebugEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.platform.standalone.gui;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public enum ANSIColor {
|
||||||
|
// Normal colors
|
||||||
|
BLACK("(0;)?30(0;)?m", Color.getHSBColor(0.000f, 0.000f, 0.000f)),
|
||||||
|
RED("(0;)?31(0;)?m", Color.getHSBColor(0.000f, 1.000f, 0.502f)),
|
||||||
|
GREEN("(0;)?32(0;)?m", Color.getHSBColor(0.333f, 1.000f, 0.502f)),
|
||||||
|
YELLOW("(0;)?33(0;)?m", Color.getHSBColor(0.167f, 1.000f, 0.502f)),
|
||||||
|
BLUE("(0;)?34(0;)?m", Color.getHSBColor(0.667f, 1.000f, 0.502f)),
|
||||||
|
MAGENTA("(0;)?35(0;)?m", Color.getHSBColor(0.833f, 1.000f, 0.502f)),
|
||||||
|
CYAN("(0;)?36(0;)?m", Color.getHSBColor(0.500f, 1.000f, 0.502f)),
|
||||||
|
WHITE("(0;)?37(0;)?m", Color.getHSBColor(0.000f, 0.000f, 0.753f)),
|
||||||
|
|
||||||
|
// Bold colors
|
||||||
|
B_BLACK("(1;30|30;1)m", Color.getHSBColor(0.000f, 0.000f, 0.502f)),
|
||||||
|
B_RED("(1;31|31;1)m", Color.getHSBColor(0.000f, 1.000f, 1.000f)),
|
||||||
|
B_GREEN("(1;32|32;1)m", Color.getHSBColor(0.333f, 1.000f, 1.000f)),
|
||||||
|
B_YELLOW("(1;33|33;1)m", Color.getHSBColor(0.167f, 1.000f, 1.000f)),
|
||||||
|
B_BLUE("(1;34|34;1)m", Color.getHSBColor(0.667f, 1.000f, 1.000f)),
|
||||||
|
B_MAGENTA("(1;35|35;1)m", Color.getHSBColor(0.833f, 1.000f, 1.000f)),
|
||||||
|
B_CYAN("(1;36|36;1)m", Color.getHSBColor(0.500f, 1.000f, 1.000f)),
|
||||||
|
B_WHITE("(1;37|37;1)m", Color.getHSBColor(0.000f, 0.000f, 1.000f)),
|
||||||
|
|
||||||
|
RESET("0m", Color.getHSBColor(0.000f, 0.000f, 1.000f));
|
||||||
|
|
||||||
|
private static final ANSIColor[] VALUES = values();
|
||||||
|
private static final String PREFIX = Pattern.quote("\u001B[");
|
||||||
|
|
||||||
|
private final String ANSICode;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private final Color color;
|
||||||
|
|
||||||
|
ANSIColor(String ANSICode, Color color) {
|
||||||
|
this.ANSICode = ANSICode;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ANSIColor fromANSI(String code) {
|
||||||
|
for (ANSIColor value : VALUES) {
|
||||||
|
if (code.matches(PREFIX + value.ANSICode)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_WHITE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.platform.standalone.gui;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.text.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class was based on this code: https://stackoverflow.com/a/6899478/5299903
|
||||||
|
*/
|
||||||
|
public class ColorPane extends JTextPane {
|
||||||
|
private static Color colorCurrent = ANSIColor.RESET.getColor();
|
||||||
|
private String remaining = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the given string in the given color to the text pane
|
||||||
|
* @param c The color
|
||||||
|
* @param s The text
|
||||||
|
*/
|
||||||
|
private void append(Color c, String s) {
|
||||||
|
StyleContext sc = StyleContext.getDefaultStyleContext();
|
||||||
|
AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, c);
|
||||||
|
int len = getDocument().getLength();
|
||||||
|
|
||||||
|
try {
|
||||||
|
getDocument().insertString(len, s, aset);
|
||||||
|
} catch (BadLocationException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the ANSI color codes from the string and add each part to the text pane
|
||||||
|
*
|
||||||
|
* @param s The text to parse
|
||||||
|
*/
|
||||||
|
public void appendANSI(String s) { // convert ANSI color codes first
|
||||||
|
int aPos = 0; // current char position in addString
|
||||||
|
int aIndex = 0; // index of next Escape sequence
|
||||||
|
int mIndex = 0; // index of "m" terminating Escape sequence
|
||||||
|
String tmpString = "";
|
||||||
|
boolean stillSearching = true; // true until no more Escape sequences
|
||||||
|
String addString = remaining + s;
|
||||||
|
remaining = "";
|
||||||
|
|
||||||
|
if (addString.length() > 0) {
|
||||||
|
aIndex = addString.indexOf("\u001B"); // find first escape
|
||||||
|
if (aIndex == -1) { // no escape/color change in this string, so just send it with current color
|
||||||
|
append(colorCurrent, addString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// otherwise There is an escape character in the string, so we must process it
|
||||||
|
|
||||||
|
if (aIndex > 0) { // Escape is not first char, so send text up to first escape
|
||||||
|
tmpString = addString.substring(0, aIndex);
|
||||||
|
append(colorCurrent, tmpString);
|
||||||
|
aPos = aIndex; // aPos is now at the beginning of the first escape sequence
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// while there's text in the input buffer
|
||||||
|
stillSearching = true;
|
||||||
|
while (stillSearching) {
|
||||||
|
mIndex = addString.indexOf("m", aPos); // find the end of the escape sequence
|
||||||
|
if (mIndex < 0) { // the buffer ends halfway through the ansi string!
|
||||||
|
remaining = addString.substring(aPos, addString.length());
|
||||||
|
stillSearching = false;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
tmpString = addString.substring(aPos, mIndex+1);
|
||||||
|
colorCurrent = ANSIColor.fromANSI(tmpString).getColor();
|
||||||
|
}
|
||||||
|
aPos = mIndex + 1;
|
||||||
|
// now we have the color, send text that is in that color (up to next escape)
|
||||||
|
|
||||||
|
aIndex = addString.indexOf("\u001B", aPos);
|
||||||
|
|
||||||
|
if (aIndex == -1) { // if that was the last sequence of the input, send remaining text
|
||||||
|
tmpString = addString.substring(aPos, addString.length());
|
||||||
|
append(colorCurrent, tmpString);
|
||||||
|
stillSearching = false;
|
||||||
|
continue; // jump out of loop early, as the whole string has been sent now
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is another escape sequence, so send part of the string and prepare for the next
|
||||||
|
tmpString = addString.substring(aPos, aIndex);
|
||||||
|
aPos = aIndex;
|
||||||
|
append(colorCurrent, tmpString);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,336 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.platform.standalone.gui;
|
||||||
|
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.command.GeyserCommand;
|
||||||
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.platform.standalone.GeyserStandaloneLogger;
|
||||||
|
import org.geysermc.platform.standalone.command.GeyserCommandManager;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class GeyserStandaloneGUI {
|
||||||
|
|
||||||
|
private static final String[] playerTableHeadings = new String[] {"IP", "Username"};
|
||||||
|
private static final List<Integer> ramValues = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final ColorPane consolePane = new ColorPane();
|
||||||
|
private static final GraphPanel ramGraph = new GraphPanel();
|
||||||
|
private static final JTable playerTable = new JTable(new String[][] { }, playerTableHeadings);
|
||||||
|
private static final int originalFontSize = consolePane.getFont().getSize();
|
||||||
|
|
||||||
|
private static final long MEGABYTE = 1024L * 1024L;
|
||||||
|
|
||||||
|
private final JMenu commandsMenu;
|
||||||
|
private final JMenu optionsMenu;
|
||||||
|
|
||||||
|
public GeyserStandaloneGUI() {
|
||||||
|
// Create the frame and setup basic settings
|
||||||
|
JFrame frame = new JFrame("Geyser Standalone");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||||
|
frame.setSize(800, 400);
|
||||||
|
frame.setMinimumSize(frame.getSize());
|
||||||
|
|
||||||
|
// Remove Java UI look
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
|
} catch (Exception ignored) { }
|
||||||
|
|
||||||
|
// Show a confirm dialog on close
|
||||||
|
frame.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent we)
|
||||||
|
{
|
||||||
|
String[] buttons = {"Yes", "No"};
|
||||||
|
int result = JOptionPane.showOptionDialog(frame, "Are you sure you want to exit?", frame.getTitle(), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, buttons, buttons[1]);
|
||||||
|
if (result == JOptionPane.YES_OPTION) {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Container cp = frame.getContentPane();
|
||||||
|
|
||||||
|
// Fetch and set the icon for the frame
|
||||||
|
URL image = getClass().getClassLoader().getResource("icon.png");
|
||||||
|
if (image != null) {
|
||||||
|
ImageIcon icon = new ImageIcon(image);
|
||||||
|
frame.setIconImage(icon.getImage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the split pane and event listeners
|
||||||
|
JSplitPane splitPane = new JSplitPane();
|
||||||
|
splitPane.setDividerLocation(600);
|
||||||
|
splitPane.addPropertyChangeListener("dividerLocation", e -> splitPaneLimit((JSplitPane)e.getSource()));
|
||||||
|
splitPane.addComponentListener(new ComponentAdapter() {
|
||||||
|
public void componentResized(ComponentEvent e) {
|
||||||
|
splitPaneLimit((JSplitPane)e.getSource());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cp.add(splitPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// Set the background and disable input for the text pane
|
||||||
|
consolePane.setBackground(Color.BLACK);
|
||||||
|
consolePane.setEditable(false);
|
||||||
|
|
||||||
|
// Wrap the text pane in a scroll pane and add it to the form
|
||||||
|
JScrollPane consoleScrollPane = new JScrollPane(consolePane);
|
||||||
|
//cp.add(consoleScrollPane, BorderLayout.CENTER);
|
||||||
|
splitPane.setLeftComponent(consoleScrollPane);
|
||||||
|
|
||||||
|
// Create a new menu bar for the top of the frame
|
||||||
|
JMenuBar menuBar = new JMenuBar();
|
||||||
|
|
||||||
|
// Create 'File'
|
||||||
|
JMenu fileMenu = new JMenu("File");
|
||||||
|
fileMenu.setMnemonic(KeyEvent.VK_F);
|
||||||
|
menuBar.add(fileMenu);
|
||||||
|
|
||||||
|
// 'Open Geyser folder' button
|
||||||
|
JMenuItem openButton = new JMenuItem("Open Geyser folder", KeyEvent.VK_O);
|
||||||
|
openButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
|
||||||
|
openButton.addActionListener(e -> {
|
||||||
|
try {
|
||||||
|
Desktop.getDesktop().open(new File("./"));
|
||||||
|
} catch (IOException ignored) { }
|
||||||
|
});
|
||||||
|
fileMenu.add(openButton);
|
||||||
|
|
||||||
|
fileMenu.addSeparator();
|
||||||
|
|
||||||
|
// 'Exit' button
|
||||||
|
JMenuItem exitButton = new JMenuItem("Exit", KeyEvent.VK_X);
|
||||||
|
exitButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK));
|
||||||
|
exitButton.addActionListener(e -> System.exit(0));
|
||||||
|
fileMenu.add(exitButton);
|
||||||
|
|
||||||
|
// Create 'Commands'
|
||||||
|
commandsMenu = new JMenu("Commands");
|
||||||
|
commandsMenu.setMnemonic(KeyEvent.VK_C);
|
||||||
|
menuBar.add(commandsMenu);
|
||||||
|
|
||||||
|
// Create 'View'
|
||||||
|
JMenu viewMenu = new JMenu("View");
|
||||||
|
viewMenu.setMnemonic(KeyEvent.VK_V);
|
||||||
|
menuBar.add(viewMenu);
|
||||||
|
|
||||||
|
// 'Zoom in' button
|
||||||
|
JMenuItem zoomInButton = new JMenuItem("Zoom In");
|
||||||
|
zoomInButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, InputEvent.CTRL_DOWN_MASK));
|
||||||
|
zoomInButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() + 1)));
|
||||||
|
viewMenu.add(zoomInButton);
|
||||||
|
|
||||||
|
// 'Zoom in' button
|
||||||
|
JMenuItem zoomOutButton = new JMenuItem("Zoom Out");
|
||||||
|
zoomOutButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_MINUS, InputEvent.CTRL_DOWN_MASK));
|
||||||
|
zoomOutButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), consolePane.getFont().getSize() - 1)));
|
||||||
|
viewMenu.add(zoomOutButton);
|
||||||
|
|
||||||
|
// 'Reset Zoom' button
|
||||||
|
JMenuItem resetZoomButton = new JMenuItem("Reset Zoom");
|
||||||
|
resetZoomButton.addActionListener(e -> consolePane.setFont(new Font(consolePane.getFont().getName(), consolePane.getFont().getStyle(), originalFontSize)));
|
||||||
|
viewMenu.add(resetZoomButton);
|
||||||
|
|
||||||
|
// create 'Options'
|
||||||
|
optionsMenu = new JMenu("Options");
|
||||||
|
viewMenu.setMnemonic(KeyEvent.VK_O);
|
||||||
|
menuBar.add(optionsMenu);
|
||||||
|
|
||||||
|
// Set the frames menu bar
|
||||||
|
frame.setJMenuBar(menuBar);
|
||||||
|
|
||||||
|
JPanel rightPane = new JPanel();
|
||||||
|
rightPane.setLayout(new CardLayout(5, 5));
|
||||||
|
//cp.add(rightPane, BorderLayout.EAST);
|
||||||
|
splitPane.setRightComponent(rightPane);
|
||||||
|
|
||||||
|
JPanel rightContentPane = new JPanel();
|
||||||
|
rightContentPane.setLayout(new GridLayout(2, 1, 5, 5));
|
||||||
|
rightPane.add(rightContentPane);
|
||||||
|
|
||||||
|
// Set the ram graph to 0
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
ramValues.add(0);
|
||||||
|
}
|
||||||
|
ramGraph.setValues(ramValues);
|
||||||
|
ramGraph.setXLabel("Loading...");
|
||||||
|
rightContentPane.add(ramGraph);
|
||||||
|
|
||||||
|
JScrollPane playerScrollPane = new JScrollPane(playerTable);
|
||||||
|
rightContentPane.add(playerScrollPane);
|
||||||
|
|
||||||
|
// This has to be done last
|
||||||
|
frame.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queue up an update to the text pane so we don't block the main thread
|
||||||
|
*
|
||||||
|
* @param text The text to append
|
||||||
|
*/
|
||||||
|
private void updateTextPane(final String text) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
consolePane.appendANSI(text);
|
||||||
|
Document doc = consolePane.getDocument();
|
||||||
|
consolePane.setCaretPosition(doc.getLength());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect the default io streams to the text pane
|
||||||
|
*/
|
||||||
|
public void redirectSystemStreams() {
|
||||||
|
// Setup a new output stream to forward it to the text pane
|
||||||
|
OutputStream out = new OutputStream() {
|
||||||
|
@Override
|
||||||
|
public void write(final int b) {
|
||||||
|
updateTextPane(String.valueOf((char) b));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b, int off, int len) {
|
||||||
|
updateTextPane(new String(b, off, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte[] b) {
|
||||||
|
write(b, 0, b.length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Override the system output streams
|
||||||
|
System.setOut(new PrintStream(out, true));
|
||||||
|
System.setErr(new PrintStream(out, true));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all the Geyser commands to the commands menu, and setup the debug mode toggle
|
||||||
|
*
|
||||||
|
* @param geyserStandaloneLogger The current logger
|
||||||
|
* @param geyserCommandManager The commands manager
|
||||||
|
*/
|
||||||
|
public void setupInterface(GeyserStandaloneLogger geyserStandaloneLogger, GeyserCommandManager geyserCommandManager) {
|
||||||
|
commandsMenu.removeAll();
|
||||||
|
|
||||||
|
for (Map.Entry<String, GeyserCommand> command : geyserCommandManager.getCommands().entrySet()) {
|
||||||
|
// Remove the offhand command and any alias commands to prevent duplicates in the list
|
||||||
|
if ("offhand".equals(command.getValue().getName()) || command.getValue().getAliases().contains(command.getKey())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the button that runs the command
|
||||||
|
JMenuItem commandButton = new JMenuItem(command.getValue().getName());
|
||||||
|
commandButton.getAccessibleContext().setAccessibleDescription(command.getValue().getDescription());
|
||||||
|
commandButton.addActionListener(e -> command.getValue().execute(geyserStandaloneLogger, new String[]{ }));
|
||||||
|
commandsMenu.add(commandButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'Debug Mode' toggle
|
||||||
|
JCheckBoxMenuItem debugMode = new JCheckBoxMenuItem("Debug Mode");
|
||||||
|
debugMode.setSelected(geyserStandaloneLogger.isDebug());
|
||||||
|
debugMode.addActionListener(e -> geyserStandaloneLogger.setDebug(!geyserStandaloneLogger.isDebug()));
|
||||||
|
optionsMenu.add(debugMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the thread to update the form information every 1s
|
||||||
|
*/
|
||||||
|
public void startUpdateThread() {
|
||||||
|
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
|
Runnable periodicTask = () -> {
|
||||||
|
if (GeyserConnector.getInstance() != null) {
|
||||||
|
// Update player table
|
||||||
|
String[][] playerNames = new String[GeyserConnector.getInstance().getPlayers().size()][2];
|
||||||
|
int i = 0;
|
||||||
|
for (Map.Entry<InetSocketAddress, GeyserSession> player : GeyserConnector.getInstance().getPlayers().entrySet()) {
|
||||||
|
playerNames[i][0] = player.getKey().getHostName();
|
||||||
|
playerNames[i][1] = player.getValue().getPlayerEntity().getUsername();
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultTableModel model = new DefaultTableModel(playerNames, playerTableHeadings);
|
||||||
|
playerTable.setModel(model);
|
||||||
|
model.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update ram graph
|
||||||
|
final long freeMemory = Runtime.getRuntime().freeMemory();
|
||||||
|
final long totalMemory = Runtime.getRuntime().totalMemory();
|
||||||
|
final int freePercent = (int)(freeMemory * 100.0 / totalMemory + 0.5);
|
||||||
|
ramValues.add(100 - freePercent);
|
||||||
|
|
||||||
|
ramGraph.setXLabel("Usage: " + String.format("%,d", (totalMemory - freeMemory) / MEGABYTE) + "mb (" + freePercent + "% free)");
|
||||||
|
|
||||||
|
// Trim the list
|
||||||
|
int k = ramValues.size();
|
||||||
|
if ( k > 10 )
|
||||||
|
ramValues.subList(0, k - 10).clear();
|
||||||
|
|
||||||
|
// Update the graph
|
||||||
|
ramGraph.setValues(ramValues);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SwingUtilities.invokeLater is called so we don't run into threading issues with the GUI
|
||||||
|
executor.scheduleAtFixedRate(() -> SwingUtilities.invokeLater(periodicTask), 0, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the JSplitPane divider is within a set of bounds
|
||||||
|
*
|
||||||
|
* @param splitPane The JSplitPane to check
|
||||||
|
*/
|
||||||
|
private void splitPaneLimit(JSplitPane splitPane) {
|
||||||
|
JRootPane frame = splitPane.getRootPane();
|
||||||
|
int location = splitPane.getDividerLocation();
|
||||||
|
if (location < frame.getWidth() - frame.getWidth() * 0.4f) {
|
||||||
|
splitPane.setDividerLocation(Math.round(frame.getWidth() - frame.getWidth() * 0.4f));
|
||||||
|
} else if (location > frame.getWidth() - 200) {
|
||||||
|
splitPane.setDividerLocation(frame.getWidth() - 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.platform.standalone.gui;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This has been modified to fit Geyser more but is based on
|
||||||
|
* https://gist.github.com/roooodcastro/6325153#gistcomment-3107524
|
||||||
|
*/
|
||||||
|
public final class GraphPanel extends JPanel {
|
||||||
|
private final static int padding = 10;
|
||||||
|
private final static int labelPadding = 25;
|
||||||
|
private final static int pointWidth = 4;
|
||||||
|
private final static int numberYDivisions = 10;
|
||||||
|
private final static Color lineColor = new Color(44, 102, 230, 180);
|
||||||
|
private final static Color pointColor = new Color(100, 100, 100, 180);
|
||||||
|
private final static Color gridColor = new Color(200, 200, 200, 200);
|
||||||
|
private static final Stroke graphStroke = new BasicStroke(2f);
|
||||||
|
private List<Integer> values = new ArrayList<>(10);
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private String xLabel = "";
|
||||||
|
|
||||||
|
public GraphPanel() {
|
||||||
|
setPreferredSize(new Dimension(200 - (padding * 2), 150 - (padding * 2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValues(Collection<Integer> newValues) {
|
||||||
|
values.clear();
|
||||||
|
addValues(newValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addValues(Collection<Integer> newValues) {
|
||||||
|
values.addAll(newValues);
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics graphics) {
|
||||||
|
super.paintComponent(graphics);
|
||||||
|
if (!(graphics instanceof Graphics2D)) {
|
||||||
|
graphics.drawString("Graphics is not Graphics2D, unable to render", 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final Graphics2D g = (Graphics2D) graphics;
|
||||||
|
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
final int length = values.size();
|
||||||
|
final int width = getWidth();
|
||||||
|
final int height = getHeight();
|
||||||
|
final int maxScore = getMaxScore();
|
||||||
|
final int minScore = getMinScore();
|
||||||
|
final int scoreRange = maxScore - minScore;
|
||||||
|
|
||||||
|
// draw white background
|
||||||
|
g.setColor(Color.WHITE);
|
||||||
|
g.fillRect(
|
||||||
|
padding + labelPadding,
|
||||||
|
padding,
|
||||||
|
width - (2 * padding) - labelPadding,
|
||||||
|
height - 2 * padding - labelPadding);
|
||||||
|
g.setColor(Color.BLACK);
|
||||||
|
|
||||||
|
final FontMetrics fontMetrics = g.getFontMetrics();
|
||||||
|
final int fontHeight = fontMetrics.getHeight();
|
||||||
|
|
||||||
|
// create hatch marks and grid lines for y axis.
|
||||||
|
for (int i = 0; i < numberYDivisions + 1; i++) {
|
||||||
|
final int x1 = padding + labelPadding;
|
||||||
|
final int x2 = pointWidth + padding + labelPadding;
|
||||||
|
final int y = height - ((i * (height - padding * 2 - labelPadding)) / numberYDivisions + padding + labelPadding);
|
||||||
|
if (length > 0) {
|
||||||
|
g.setColor(gridColor);
|
||||||
|
g.drawLine(padding + labelPadding + 1 + pointWidth, y, width - padding, y);
|
||||||
|
|
||||||
|
g.setColor(Color.BLACK);
|
||||||
|
final int tickValue = (int) (minScore + ((scoreRange * i) / numberYDivisions));
|
||||||
|
final String yLabel = tickValue + "";
|
||||||
|
final int labelWidth = fontMetrics.stringWidth(yLabel);
|
||||||
|
g.drawString(yLabel, x1 - labelWidth - 5, y + (fontHeight / 2) - 3);
|
||||||
|
}
|
||||||
|
g.drawLine(x1, y, x2, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and for x axis
|
||||||
|
if (length > 1) {
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
final int x = i * (width - padding * 2 - labelPadding) / (length - 1) + padding + labelPadding;
|
||||||
|
final int y1 = height - padding - labelPadding;
|
||||||
|
final int y2 = y1 - pointWidth;
|
||||||
|
if ((i % ((int) ((length / 20.0)) + 1)) == 0) {
|
||||||
|
g.setColor(gridColor);
|
||||||
|
g.drawLine(x, height - padding - labelPadding - 1 - pointWidth, x, padding);
|
||||||
|
|
||||||
|
g.setColor(Color.BLACK);
|
||||||
|
|
||||||
|
/*g.setColor(Color.BLACK);
|
||||||
|
final String xLabel = i + "";
|
||||||
|
final int labelWidth = fontMetrics.stringWidth(xLabel);
|
||||||
|
g.drawString(xLabel, x - labelWidth / 2, y1 + fontHeight + 3);*/
|
||||||
|
}
|
||||||
|
g.drawLine(x, y1, x, y2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create x and y axes
|
||||||
|
g.drawLine(padding + labelPadding, height - padding - labelPadding, padding + labelPadding, padding);
|
||||||
|
g.drawLine(padding + labelPadding, height - padding - labelPadding, width - padding, height - padding - labelPadding);
|
||||||
|
|
||||||
|
g.setColor(Color.BLACK);
|
||||||
|
final int labelWidth = fontMetrics.stringWidth(xLabel);
|
||||||
|
final int labelX = ((padding + labelPadding) + (width - padding)) / 2;
|
||||||
|
final int labelY = height - padding - labelPadding;
|
||||||
|
g.drawString(xLabel, labelX - labelWidth / 2, labelY + fontHeight + 3);
|
||||||
|
|
||||||
|
final Stroke oldStroke = g.getStroke();
|
||||||
|
g.setColor(lineColor);
|
||||||
|
g.setStroke(graphStroke);
|
||||||
|
|
||||||
|
final double xScale = ((double) width - (2 * padding) - labelPadding) / (length - 1);
|
||||||
|
final double yScale = ((double) height - 2 * padding - labelPadding) / scoreRange;
|
||||||
|
|
||||||
|
final List<Point> graphPoints = new ArrayList<>(length);
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
final int x1 = (int) (i * xScale + padding + labelPadding);
|
||||||
|
final int y1 = (int) ((maxScore - values.get(i)) * yScale + padding);
|
||||||
|
graphPoints.add(new Point(x1, y1));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < graphPoints.size() - 1; i++) {
|
||||||
|
final int x1 = graphPoints.get(i).x;
|
||||||
|
final int y1 = graphPoints.get(i).y;
|
||||||
|
final int x2 = graphPoints.get(i + 1).x;
|
||||||
|
final int y2 = graphPoints.get(i + 1).y;
|
||||||
|
g.drawLine(x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean drawDots = width > (length * pointWidth);
|
||||||
|
if (drawDots) {
|
||||||
|
g.setStroke(oldStroke);
|
||||||
|
g.setColor(pointColor);
|
||||||
|
for (Point graphPoint : graphPoints) {
|
||||||
|
final int x = graphPoint.x - pointWidth / 2;
|
||||||
|
final int y = graphPoint.y - pointWidth / 2;
|
||||||
|
g.fillOval(x, y, pointWidth, pointWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMinScore() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getMaxScore() {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
}
|
BIN
bootstrap/standalone/src/main/resources/icon.png
Normal file
BIN
bootstrap/standalone/src/main/resources/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 113 KiB |
|
@ -1,9 +1,12 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Configuration status="WARN">
|
<Configuration status="WARN">
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<TerminalConsole name="Console">
|
<TerminalConsole name="TerminalConsole">
|
||||||
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red dark, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red dark, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
||||||
</TerminalConsole>
|
</TerminalConsole>
|
||||||
|
<Console name="Console" target="SYSTEM_OUT" follow="true">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss} %style{%highlight{%level}{FATAL=red dark, ERROR=red, WARN=yellow bright, INFO=cyan bright, DEBUG=green, TRACE=white}}] %minecraftFormatting{%msg}%n"/>
|
||||||
|
</Console>
|
||||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||||
<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %level{length=1} - %msg%n"/>
|
<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %level{length=1} - %msg%n"/>
|
||||||
<Policies>
|
<Policies>
|
||||||
|
@ -14,6 +17,7 @@
|
||||||
</Appenders>
|
</Appenders>
|
||||||
<Loggers>
|
<Loggers>
|
||||||
<Root level="INFO">
|
<Root level="INFO">
|
||||||
|
<AppenderRef ref="TerminalConsole"/>
|
||||||
<AppenderRef ref="Console"/>
|
<AppenderRef ref="Console"/>
|
||||||
<AppenderRef ref="File"/>
|
<AppenderRef ref="File"/>
|
||||||
</Root>
|
</Root>
|
||||||
|
|
|
@ -44,17 +44,17 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.BiomeTranslator;
|
import org.geysermc.connector.network.translators.BiomeTranslator;
|
||||||
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
import org.geysermc.connector.network.translators.EntityIdentifierRegistry;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||||
|
import org.geysermc.connector.network.translators.effect.EffectRegistry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
import org.geysermc.connector.network.translators.item.ItemRegistry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry;
|
import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry;
|
||||||
|
import org.geysermc.connector.network.translators.sound.SoundRegistry;
|
||||||
import org.geysermc.connector.network.translators.world.WorldManager;
|
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||||
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||||
import org.geysermc.connector.network.translators.effect.EffectRegistry;
|
|
||||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||||
import org.geysermc.connector.utils.DimensionUtils;
|
import org.geysermc.connector.utils.DimensionUtils;
|
||||||
import org.geysermc.connector.utils.DockerCheck;
|
import org.geysermc.connector.utils.DockerCheck;
|
||||||
import org.geysermc.connector.utils.LocaleUtils;
|
import org.geysermc.connector.utils.LocaleUtils;
|
||||||
import org.geysermc.connector.network.translators.sound.SoundRegistry;
|
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
|
@ -158,8 +158,23 @@ public class GeyserConnector {
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
|
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isGui = false;
|
||||||
|
// This will check if we are in standalone and get the 'useGui' variable from there
|
||||||
|
if (platformType == PlatformType.STANDALONE) {
|
||||||
|
try {
|
||||||
|
Class<?> cls = Class.forName("org.geysermc.platform.standalone.GeyserStandaloneBootstrap");
|
||||||
|
isGui = (boolean) cls.getMethod("isUseGui").invoke(cls.cast(bootstrap));
|
||||||
|
} catch (Exception e) { e.printStackTrace(); }
|
||||||
|
}
|
||||||
|
|
||||||
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
||||||
logger.info(String.format("Done (%ss)! Run /geyser help for help!", new DecimalFormat("#.###").format(completeTime)));
|
String message = String.format("Done (%ss)!", new DecimalFormat("#.###").format(completeTime));
|
||||||
|
if (isGui) {
|
||||||
|
message += " Run Commands -> help for help!";
|
||||||
|
} else {
|
||||||
|
message += " Run /geyser help for help!";
|
||||||
|
}
|
||||||
|
logger.info(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
|
|
Loading…
Reference in a new issue