mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-23 06:50:26 +01:00
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>
|
||||
<groupId>net.minecrell</groupId>
|
||||
<artifactId>terminalconsoleappender</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.2.0</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
|
|
|
@ -25,16 +25,23 @@
|
|||
|
||||
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.bootstrap.GeyserBootstrap;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.common.PlatformType;
|
||||
import org.geysermc.connector.command.CommandManager;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.dump.BootstrapDumpInfo;
|
||||
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.connector.ping.GeyserLegacyPingPassthrough;
|
||||
import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||
import org.geysermc.connector.utils.FileUtils;
|
||||
import org.geysermc.platform.standalone.command.GeyserCommandManager;
|
||||
import org.geysermc.platform.standalone.gui.GeyserStandaloneGUI;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -49,14 +56,49 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
private GeyserStandaloneLogger geyserLogger;
|
||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||
|
||||
private GeyserStandaloneGUI gui;
|
||||
|
||||
@Getter
|
||||
private boolean useGui = System.console() == null;
|
||||
|
||||
private GeyserConnector connector;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public void onEnable(boolean useGui) {
|
||||
this.useGui = useGui;
|
||||
this.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
|
||||
LoopbackUtil.checkLoopback(geyserLogger);
|
||||
|
@ -73,9 +115,15 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
connector = GeyserConnector.start(PlatformType.STANDALONE, this);
|
||||
geyserCommandManager = new GeyserCommandManager(connector);
|
||||
|
||||
if (gui != null) {
|
||||
gui.setupInterface(geyserLogger, geyserCommandManager);
|
||||
}
|
||||
|
||||
geyserPingPassthrough = GeyserLegacyPingPassthrough.init(connector);
|
||||
|
||||
geyserLogger.start();
|
||||
if (!useGui) {
|
||||
geyserLogger.start(); // Throws an error otherwise
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,6 +30,7 @@ import lombok.extern.log4j.Log4j2;
|
|||
|
||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
|
@ -96,7 +97,15 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements org
|
|||
|
||||
@Override
|
||||
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
|
||||
|
|
|
@ -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"?>
|
||||
<Configuration status="WARN">
|
||||
<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"/>
|
||||
</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">
|
||||
<PatternLayout pattern="%d{yyy-MM-dd HH:mm:ss.SSS} [%t] %level{length=1} - %msg%n"/>
|
||||
<Policies>
|
||||
|
@ -14,6 +17,7 @@
|
|||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="INFO">
|
||||
<AppenderRef ref="TerminalConsole"/>
|
||||
<AppenderRef ref="Console"/>
|
||||
<AppenderRef ref="File"/>
|
||||
</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.EntityIdentifierRegistry;
|
||||
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.ItemTranslator;
|
||||
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.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.effect.EffectRegistry;
|
||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
import org.geysermc.connector.utils.DockerCheck;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
import org.geysermc.connector.network.translators.sound.SoundRegistry;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.text.DecimalFormat;
|
||||
|
@ -158,8 +158,23 @@ public class GeyserConnector {
|
|||
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;
|
||||
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() {
|
||||
|
|
Loading…
Reference in a new issue