Исходный код может быть не обновлен до самой новой версии, но вы можете написать нам и мы выдадим точно последнею версию
мы максимально поддерживаем наш проект и надеемся что вы даже обычный повседневный пользователь примите участие в нашем проекте
Исходный код может быть не обновлен до самой новой версии, но вы можете написать нам и мы выдадим точно последнею версию
мы максимально поддерживаем наш проект и надеемся что вы даже обычный повседневный пользователь примите участие в нашем проекте
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.nio.charset.StandardCharsets;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.io.*;
import java.net.*;
import javax.swing.border.*;
import java.util.zip.*;
import java.nio.file.*;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Collections;
import java.util.Comparator;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import javax.swing.filechooser.FileNameExtensionFilter;
public class SGLauncher {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new LauncherGUI());
}
}
class Account implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private String email;
private String passwordHash;
private String avatarPath;
private String wallpaperPath;
private boolean rememberMe;
public Account(String username, String email, String password) {
this.username = username;
this.email = email;
this.passwordHash = hashPassword(password);
this.avatarPath = "";
this.wallpaperPath = "";
this.rememberMe = false;
}
private String hashPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hash = md.digest(password.getBytes(StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
for (byte b : hash) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (Exception e) {
throw new RuntimeException("Ошибка хеширования пароля", e);
}
}
public boolean verifyPassword(String password) {
return this.passwordHash.equals(hashPassword(password));
}
public String getUsername() { return username; }
public String getEmail() { return email; }
public String getAvatarPath() { return avatarPath; }
public void setAvatarPath(String path) { this.avatarPath = path; }
public String getWallpaperPath() { return wallpaperPath; }
public void setWallpaperPath(String path) { this.wallpaperPath = path; }
public boolean getRememberMe() { return rememberMe; }
public void setRememberMe(boolean remember) { this.rememberMe = remember; }
}
class Game implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String version;
private String pathToExecutable;
private String cloudUrl;
private boolean isSteamGame;
private String steamAppId;
private long lastPlayed;
private int playCount;
public Game(String name, String version, String pathToExecutable, String cloudUrl) {
this(name, version, pathToExecutable, cloudUrl, false, "");
}
public Game(String name, String version, String pathToExecutable, String cloudUrl, boolean isSteamGame, String steamAppId) {
this.name = name;
this.version = version;
this.pathToExecutable = pathToExecutable;
this.cloudUrl = cloudUrl;
this.isSteamGame = isSteamGame;
this.steamAppId = steamAppId;
this.lastPlayed = 0;
this.playCount = 0;
}
public String getName() { return name; }
public String getVersion() { return version; }
public String getPathToExecutable() { return pathToExecutable; }
public String getCloudUrl() { return cloudUrl; }
public boolean hasCloudDownload() { return cloudUrl != null && !cloudUrl.isEmpty(); }
public boolean isSteamGame() { return isSteamGame; }
public String getSteamAppId() { return steamAppId; }
public long getLastPlayed() { return lastPlayed; }
public int getPlayCount() { return playCount; }
public void incrementPlayCount() { playCount++; }
public void setLastPlayed(long time) { lastPlayed = time; }
}
class LauncherGUI {
private JFrame frame;
private JList<Game> gameList;
private DefaultListModel<Game> listModel;
private List<Game> games = new ArrayList<>();
private static final String GAMES_DIR = "C:/Games";
private static final String SAVE_FILE = "games.dat";
private static final String SETTINGS_FILE = "settings.dat";
private static final String STATS_FILE = "stats.dat";
private static final String ACCOUNTS_FILE = "accounts.dat";
private static final String LAST_ACCOUNT_FILE = "last_account.dat";
private JTextPane descriptionPane;
private Color currentButtonColor = new Color(255, 150, 0);
private Color currentBackgroundColor = new Color(30, 30, 30);
private Color currentSecondaryColor = new Color(70, 70, 70);
private Color currentTextColor = Color.WHITE;
private boolean darkMode = true;
private Timer factsTimer;
private Random random = new Random();
private List<String> interestingFacts = new ArrayList<>();
private int totalLaunches = 0;
private long firstLaunchTime = System.currentTimeMillis();
private JLabel statusLabel;
private JPanel buttonPanel;
// Аккаунт система
private Account currentAccount;
private boolean isLoggedIn = false;
private JPanel accountPanel;
private JLabel avatarLabel;
private JLabel usernameLabel;
private JLabel emailLabel;
private JLabel wallpaperLabel;
// Иконки
private ImageIcon settingsIcon;
private ImageIcon guestIcon;
public LauncherGUI() {
loadIcons();
createGamesDirectory();
loadSettings();
loadStats();
setupInterestingFacts();
setupWindow();
loadGames();
setupUI();
addCreditsLabel();
startFactsTimer();
// Автоматический вход при запуске
autoLogin();
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
saveGames();
saveSettings();
saveStats();
if (factsTimer != null) {
factsTimer.cancel();
}
}
});
incrementTotalLaunches();
}
private void loadIcons() {
try {
// Загрузка иконки настроек
File settingsFile = new File("data/setings.png");
if (settingsFile.exists()) {
Image img = ImageIO.read(settingsFile);
settingsIcon = new ImageIcon(img.getScaledInstance(32, 32, Image.SCALE_SMOOTH));
} else {
// Создаем заглушку если файл не найден
BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.GRAY);
g2d.fillRect(0, 0, 32, 32);
g2d.setColor(Color.WHITE);
g2d.setFont(new Font("Arial", Font.BOLD, 12));
g2d.drawString("⚙", 8, 20);
g2d.dispose();
settingsIcon = new ImageIcon(img);
}
// Загрузка иконки гостя
File guestFile = new File("data/Guest.png");
if (guestFile.exists()) {
Image img = ImageIO.read(guestFile);
guestIcon = new ImageIcon(img.getScaledInstance(32, 32, Image.SCALE_SMOOTH));
} else {
// Создаем заглушку если файл не найден
BufferedImage img = new BufferedImage(32, 32, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.GRAY);
g2d.fillOval(0, 0, 32, 32);
g2d.setColor(Color.WHITE);
g2d.setFont(new Font("Arial", Font.BOLD, 12));
g2d.drawString("G", 12, 20);
g2d.dispose();
guestIcon = new ImageIcon(img);
}
} catch (Exception e) {
System.err.println("Ошибка загрузки иконок: " + e.getMessage());
}
}
private void autoLogin() {
File lastAccountFile = new File(LAST_ACCOUNT_FILE);
if (lastAccountFile.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(LAST_ACCOUNT_FILE))) {
String lastUsername = (String) ois.readObject();
boolean rememberMe = ois.readBoolean();
if (rememberMe) {
// Пытаемся найти аккаунт и автоматически войти
File accountsFile = new File(ACCOUNTS_FILE);
if (accountsFile.exists()) {
try (ObjectInputStream accOis = new ObjectInputStream(new FileInputStream(ACCOUNTS_FILE))) {
@SuppressWarnings("unchecked")
List<Account> accounts = (List<Account>) accOis.readObject();
for (Account acc : accounts) {
if (acc.getUsername().equals(lastUsername) && acc.getRememberMe()) {
currentAccount = acc;
isLoggedIn = true;
updateAccountPanel();
updateMenuVisibility();
System.out.println("Автоматический вход выполнен для: " + lastUsername);
break;
}
}
}
}
}
} catch (Exception e) {
System.err.println("Ошибка автоматического входа: " + e.getMessage());
}
}
}
private void saveLastAccount() {
if (currentAccount != null) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(LAST_ACCOUNT_FILE))) {
oos.writeObject(currentAccount.getUsername());
oos.writeBoolean(currentAccount.getRememberMe());
} catch (IOException e) {
System.err.println("Ошибка сохранения последнего аккаунта: " + e.getMessage());
}
}
}
private void setupInterestingFacts() {
interestingFacts.add("Этот лаунчер абсолютно бесплатный, но вы можете поддержать автора через меню 'Главное меню -> Поддержать автора'");
interestingFacts.add("Лаунчер поддерживает запуск игр без Steam, даже если они требуют Steam!");
interestingFacts.add("Вы можете добавить любую игру, просто указав путь к исполняемому файлу");
interestingFacts.add("Лаунчер автоматически сохраняет все ваши настройки и список игр");
interestingFacts.add("Вы можете изменить цветовую схему лаунчера в настройках");
interestingFacts.add("Версия лаунчера: v14.1 Alpha open test");
interestingFacts.add("Лаунчер был создан одним разработчиком в свободное время");
interestingFacts.add("Вы можете сортировать игры по имени, дате запуска или количеству запусков");
interestingFacts.add("Темная тема снижает нагрузку на глаза при долгом использовании");
interestingFacts.add("Лаунчер поддерживает как .exe, так и .jar игры");
interestingFacts.add("Статистика игр сохраняется между сеансами работы лаунчера");
interestingFacts.add("Вы можете удалить любую игру через меню 'Настройки -> Удалить игру'");
interestingFacts.add("Лаунчер не собирает никаких личных данных пользователя");
interestingFacts.add("Новые функции добавляются регулярно, следите за обновлениями!");
interestingFacts.add("Вы можете предложить свои идеи для улучшения лаунчера через Telegram автора");
interestingFacts.add("Лаунчер работает на чистой Java без дополнительных зависимостей");
interestingFacts.add("Для работы Steam-игр без Steam используется специальная эмуляция");
interestingFacts.add("Все временные файлы Steam-эмуляции автоматически удаляются после закрытия игры");
interestingFacts.add("Вы можете изменить тему лаунчера на светлую в настройки");
interestingFacts.add("Лаунчер создан с любовью к игровой индустрии");
interestingFacts.add("Среднее время разработки подобного лаунчера - около 100 часов");
interestingFacts.add("Java была выбрана как кроссплатформенное решение для лаунчера");
interestingFacts.add("Лаунчер тестировался на Windows 10 и 11, а также на некоторых Linux-дистрибутивах");
interestingFacts.add("Вы можете добавить игру вручную или скачать ее из облака");
interestingFacts.add("Игры из облака автоматически обновляются при наличии новой версии");
interestingFacts.add("Лаунчер поддерживает русский и английский интерфейс");
interestingFacts.add("Все ошибки запуска игр логируются для последующего анализа");
interestingFacts.add("Вы можете изменить размер шрифта в настройках лаунчера");
interestingFacts.add("Лаунчер использует современный плоский дизайн для лучшего восприятия");
interestingFacts.add("Анимации и эффекты сведены к минимуму для максимальной производительности");
interestingFacts.add("Код лаунчера полностью написан вручную без использования конструкторов интерфейса");
interestingFacts.add("Вы можете экспортировать и импортировать список игр для резервного копирования");
interestingFacts.add("Лаунчер поддерживает игры с пробелами в путях к файлам");
interestingFacts.add("Для каждой игры ведется статистика количества запусков");
interestingFacts.add("Вы можете увидеть, когда в последний раз запускали каждую игру");
interestingFacts.add("Лаунчер автоматически определяет тип игры (обычная/Steam) при добавлении");
interestingFacts.add("Все Steam-игры помечаются специальной иконкой в списке");
interestingFacts.add("Вы можете изменить порядок отображения игр в списке");
interestingFacts.add("Лаунчер поддерживает одновременный запуск нескольких игр");
interestingFacts.add("При ошибке запуска игры вы получите подробное сообщение об ошибке");
interestingFacts.add("Лаунчер проверяет наличие файлов перед попыткой запуска игры");
interestingFacts.add("Вы можете добавить игру, просто перетащив ее исполняемый файл в лаунчер");
interestingFacts.add("Лаунчер запоминает последнюю выбранную игру между сеансами");
interestingFacts.add("Все настройки цветов сохраняются автоматически");
interestingFacts.add("Вы можете сбросить все настройки к значениям по умолчанию");
interestingFacts.add("Лаунчер поддерживает полноэкранный режим для большего удобства");
interestingFacts.add("Для разработчика важна обратная связь от пользователей лаунчера");
interestingFacts.add("Лаунчер не содержит рекламы и никогда не будет ее содержать");
interestingFacts.add("Вы можете скрыть список игр для более компактного отображения");
interestingFacts.add("Все текстовые поля поддерживают копирование текста стандартными сочетаниями клавиш");
interestingFacts.add("Лаунчер автоматически проверяет наличие новых версий при запуске");
interestingFacts.add("Вы можете вручную проверить обновления через меню 'Главное меню'");
interestingFacts.add("Для каждой игры можно установить собственную иконку");
interestingFacts.add("Лаунчер поддерживает смену темы без перезапуска");
}
private void startFactsTimer() {
factsTimer = new Timer();
factsTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
SwingUtilities.invokeLater(() -> {
if (gameList.getSelectedValue() == null) {
showRandomFact();
}
});
}
}, 5000, 5000);
}
private void showRandomFact() {
if (!interestingFacts.isEmpty()) {
String fact = interestingFacts.get(random.nextInt(interestingFacts.size()));
descriptionPane.setText("<html><body style='color:white; font-family:Arial;'>"
+ "<h2 style='color:" + String.format("#%02x%02x%02x",
currentButtonColor.getRed(),
currentButtonColor.getGreen(),
currentButtonColor.getBlue()) + "'>Интересный факт</h2>"
+ "<p>" + fact + "</p>"
+ "<p style='color:#AAAAAA; font-size:10px;'>Факты меняются каждые 5 секунд</p>"
+ "</body></html>");
}
}
private void createGamesDirectory() {
File gamesDir = new File(GAMES_DIR);
if (!gamesDir.exists()) {
if (gamesDir.mkdirs()) {
System.out.println("Created games directory: " + GAMES_DIR);
} else {
System.err.println("Failed to create games directory: " + GAMES_DIR);
}
}
}
private void saveGames() {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SAVE_FILE))) {
List<Game> gamesToSave = new ArrayList<>();
for (int i = 0; i < listModel.size(); i++) {
gamesToSave.add(listModel.get(i));
}
oos.writeObject(gamesToSave);
} catch (IOException e) {
System.err.println("Ошибка при сохранении игр: " + e.getMessage());
}
}
@SuppressWarnings("unchecked")
private void loadGames() {
File file = new File(SAVE_FILE);
if (file.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SAVE_FILE))) {
games = (List<Game>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("Ошибка при загрузке игр: " + e.getMessage());
loadDefaultGames();
}
} else {
loadDefaultGames();
}
}
private void saveSettings() {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SETTINGS_FILE))) {
oos.writeObject(currentButtonColor);
oos.writeObject(currentBackgroundColor);
oos.writeObject(currentSecondaryColor);
oos.writeObject(currentTextColor);
oos.writeBoolean(darkMode);
} catch (IOException e) {
System.err.println("Ошибка при сохранении настроек: " + e.getMessage());
}
}
@SuppressWarnings("unchecked")
private void loadSettings() {
File file = new File(SETTINGS_FILE);
if (file.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SETTINGS_FILE))) {
currentButtonColor = (Color) ois.readObject();
currentBackgroundColor = (Color) ois.readObject();
currentSecondaryColor = (Color) ois.readObject();
currentTextColor = (Color) ois.readObject();
darkMode = ois.readBoolean();
if (!darkMode) {
toggleDarkMode();
}
} catch (IOException | ClassNotFoundException e) {
System.err.println("Ошибка при загрузке настроек: " + e.getMessage());
}
}
}
private void loadStats() {
File file = new File(STATS_FILE);
if (file.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(STATS_FILE))) {
totalLaunches = ois.readInt();
firstLaunchTime = ois.readLong();
} catch (IOException e) {
System.err.println("Ошибка при загрузке статистики: " + e.getMessage());
}
}
}
private void saveStats() {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(STATS_FILE))) {
oos.writeInt(totalLaunches);
oos.writeLong(firstLaunchTime);
} catch (IOException e) {
System.err.println("Ошибка при сохранении статистики: " + e.getMessage());
}
}
private void incrementTotalLaunches() {
totalLaunches++;
updateStatusBar();
}
private void updateStatusBar() {
long daysUsed = (System.currentTimeMillis() - firstLaunchTime) / (1000 * 60 * 60 * 24);
statusLabel.setText("Запусков: " + totalLaunches + " | Дней с первого запуска: " + daysUsed);
}
private void loadDefaultGames() {
games.add(new Game("People Playground", "1.26", GAMES_DIR + "/PeoplePlayground/PeoplePlayground.exe",
"https://sites.google.com/view/sglauncher/%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0"));
games.add(new Game("Postal 2", "1.4", GAMES_DIR + "/Postal2/Postal2.exe",
"https://sites.google.com/view/sglauncher/%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0"));
games.add(new Game("Half-Life 2", "1.0", "C:/Program Files (x86)/Steam/steamapps/common/Half-Life 2/hl2.exe",
"", true, "220"));
}
private void addCreditsLabel() {
JPanel creditPanel = new JPanel(new BorderLayout());
creditPanel.setBackground(currentBackgroundColor);
JLabel creditLabel = new JLabel("<html><a href='#'>By <b>Chikatilo</b></a></html>");
creditLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
creditLabel.setForeground(currentButtonColor);
creditLabel.setFont(new Font("Arial", Font.BOLD, 12));
creditLabel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0));
creditLabel.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
try {
Desktop.getDesktop().browse(new URI("https://t.me/chikotilo12"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
@Override
public void mouseEntered(MouseEvent e) {
creditLabel.setForeground(new Color(
Math.min(255, currentButtonColor.getRed() + 50),
Math.min(255, currentButtonColor.getGreen() + 50),
Math.min(255, currentButtonColor.getBlue() + 50)
));
}
@Override
public void mouseExited(MouseEvent e) {
creditLabel.setForeground(currentButtonColor);
}
});
creditPanel.add(creditLabel, BorderLayout.WEST);
buttonPanel.add(creditPanel, 0);
}
private void setupWindow() {
frame = new JFrame("SGLauncher");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1000, 700);
frame.setMinimumSize(new Dimension(800, 600));
frame.setLocationRelativeTo(null);
JMenuBar menuBar = new JMenuBar();
// Главное меню
JMenu mainMenu = new JMenu("Главное меню");
JMenuItem supportItem = new JMenuItem("Поддержать автора");
JMenuItem checkUpdatesItem = new JMenuItem("Проверить обновления");
JMenuItem statsItem = new JMenuItem("Статистика");
JMenuItem exportItem = new JMenuItem("Экспорт списка игр");
JMenuItem importItem = new JMenuItem("Импорт списка игр");
// Меню аккаунта
JMenu accountMenu = new JMenu("Аккаунт");
JMenuItem loginItem = new JMenuItem("Войти");
JMenuItem registerItem = new JMenuItem("Зарегистрироваться");
JMenuItem logoutItem = new JMenuItem("Выйти");
JMenu changeAvatarMenu = new JMenu("Изменить аватарку");
// Добавляем стандартные иконки для аватарок
String[] avatarIcons = {"👤", "👨", "👩", "🧑", "🧔", "👨🦰", "👩🦰", "👨🦱", "👩🦱", "👨🦳", "👩🦳", "👨🦲", "👩🦲"};
for (String icon : avatarIcons) {
JMenuItem avatarItem = new JMenuItem(icon);
avatarItem.addActionListener(e -> setAvatarIcon(icon));
changeAvatarMenu.add(avatarItem);
}
// Добавляем пункт для загрузки кастомной аватарки
JMenuItem customAvatarItem = new JMenuItem("Загрузить свою аватарку");
customAvatarItem.addActionListener(e -> changeAvatar());
changeAvatarMenu.addSeparator();
changeAvatarMenu.add(customAvatarItem);
JMenuItem changeWallpaperItem = new JMenuItem("Изменить обои");
loginItem.addActionListener(e -> showCompactLoginDialog());
registerItem.addActionListener(e -> showCompactRegisterDialog());
logoutItem.addActionListener(e -> logout());
changeWallpaperItem.addActionListener(e -> changeWallpaper());
// Настройка видимости пунктов меню в зависимости от состояния входа
loginItem.setVisible(!isLoggedIn);
registerItem.setVisible(!isLoggedIn);
logoutItem.setVisible(isLoggedIn);
changeAvatarMenu.setVisible(isLoggedIn);
changeWallpaperItem.setVisible(isLoggedIn);
accountMenu.add(loginItem);
accountMenu.add(registerItem);
accountMenu.addSeparator();
accountMenu.add(changeAvatarMenu);
accountMenu.add(changeWallpaperItem);
accountMenu.addSeparator();
accountMenu.add(logoutItem);
supportItem.addActionListener(e -> {
try {
Desktop.getDesktop().browse(new URI("https://www.donationalerts.com/r/sooborn"));
} catch (Exception ex) {
showError("Не удалось открыть ссылку: " + ex.getMessage());
}
});
checkUpdatesItem.addActionListener(e -> checkForUpdates());
statsItem.addActionListener(e -> showStatsDialog());
exportItem.addActionListener(e -> exportGames());
importItem.addActionListener(e -> importGames());
mainMenu.add(supportItem);
mainMenu.add(checkUpdatesItem);
mainMenu.add(statsItem);
mainMenu.addSeparator();
mainMenu.add(exportItem);
mainMenu.add(importItem);
menuBar.add(mainMenu);
menuBar.add(accountMenu);
// Настройки
JMenu settingsMenu = new JMenu("Настройки");
JMenuItem colorSettings = new JMenuItem("Цветовая схема");
JMenuItem deleteGame = new JMenuItem("Удалить игру");
JMenuItem toggleTheme = new JMenuItem(darkMode ? "Светлая тема" : "Темная тема");
JMenuItem sortMenu = new JMenu("Сортировка");
JMenuItem sortByName = new JMenuItem("По имени");
JMenuItem sortByLastPlayed = new JMenuItem("По дате последнего запуска");
JMenuItem sortByPlayCount = new JMenuItem("По количеству запусков");
sortByName.addActionListener(e -> sortGames(Comparator.comparing(Game::getName)));
sortByLastPlayed.addActionListener(e -> sortGames(Comparator.comparingLong(Game::getLastPlayed).reversed()));
sortByPlayCount.addActionListener(e -> sortGames(Comparator.comparingInt(Game::getPlayCount).reversed()));
sortMenu.add(sortByName);
sortMenu.add(sortByLastPlayed);
sortMenu.add(sortByPlayCount);
colorSettings.addActionListener(e -> showColorSettingsDialog());
deleteGame.addActionListener(e -> deleteSelectedGame());
toggleTheme.addActionListener(e -> {
darkMode = !darkMode;
toggleTheme.setText(darkMode ? "Светлая тема" : "Темная тема");
toggleDarkMode();
});
settingsMenu.add(colorSettings);
settingsMenu.add(toggleTheme);
settingsMenu.add(sortMenu);
settingsMenu.addSeparator();
settingsMenu.add(deleteGame);
menuBar.add(settingsMenu);
frame.setJMenuBar(menuBar);
// Статус бар
statusLabel = new JLabel();
statusLabel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 5));
statusLabel.setFont(new Font("Arial", Font.PLAIN, 12));
statusLabel.setForeground(new Color(150, 150, 150));
frame.add(statusLabel, BorderLayout.SOUTH);
}
private void toggleDarkMode() {
if (darkMode) {
currentBackgroundColor = new Color(30, 30, 30);
currentSecondaryColor = new Color(70, 70, 70);
currentTextColor = Color.WHITE;
} else {
currentBackgroundColor = new Color(240, 240, 240);
currentSecondaryColor = new Color(200, 200, 200);
currentTextColor = Color.BLACK;
}
applyColors();
}
private void sortGames(Comparator<Game> comparator) {
List<Game> sortedGames = new ArrayList<>();
for (int i = 0; i < listModel.size(); i++) {
sortedGames.add(listModel.get(i));
}
Collections.sort(sortedGames, comparator);
listModel.clear();
for (Game game : sortedGames) {
listModel.addElement(game);
}
}
private void deleteSelectedGame() {
Game selectedGame = gameList.getSelectedValue();
if (selectedGame == null) {
showWarning("Выберите игру для удаления!");
return;
}
int confirm = JOptionPane.showConfirmDialog(frame,
"Удалить игру " + selectedGame.getName() + "?",
"Подтверждение удаления",
JOptionPane.YES_NO_OPTION);
if (confirm == JOptionPane.YES_OPTION) {
listModel.removeElement(selectedGame);
saveGames();
showInfo("Игра " + selectedGame.getName() + " успешно удалена");
}
}
private void showColorSettingsDialog() {
JDialog dialog = new JDialog(frame, "Настройки цветов", true);
dialog.setSize(500, 400);
dialog.setLocationRelativeTo(frame);
dialog.setLayout(new BorderLayout());
JPanel contentPanel = new JPanel(new GridLayout(0, 1, 10, 10));
contentPanel.setBackground(currentBackgroundColor);
contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
ColorTemplate[] templates = {
new ColorTemplate("Оранжевый", new Color(30, 30, 30), new Color(255, 150, 0), new Color(70, 70, 70)),
new ColorTemplate("Красный", new Color(40, 20, 20), new Color(255, 80, 80), new Color(90, 40, 40)),
new ColorTemplate("Синий", new Color(20, 20, 40), new Color(80, 80, 255), new Color(40, 40, 90)),
new ColorTemplate("Зеленый", new Color(20, 40, 20), new Color(80, 255, 80), new Color(40, 90, 40)),
new ColorTemplate("Фиолетовый", new Color(40, 20, 40), new Color(200, 100, 255), new Color(90, 40, 90)),
new ColorTemplate("Черный", new Color(10, 10, 10), new Color(100, 100, 100), new Color(50, 50, 50)),
new ColorTemplate("Белый", new Color(240, 240, 240), new Color(50, 50, 50), new Color(200, 200, 200))
};
ButtonGroup group = new ButtonGroup();
for (ColorTemplate template : templates) {
JRadioButton radioButton = new JRadioButton(template.getName());
radioButton.setBackground(currentBackgroundColor);
radioButton.setForeground(currentTextColor);
radioButton.setOpaque(true);
radioButton.addActionListener(e -> {
currentBackgroundColor = template.getBackground();
currentButtonColor = template.getButton();
currentSecondaryColor = template.getSecondary();
darkMode = !template.getName().equals("Белый");
applyColors();
});
group.add(radioButton);
contentPanel.add(radioButton);
}
JButton applyButton = new JButton("Применить");
styleOrangeButton(applyButton);
applyButton.addActionListener(e -> {
applyColors();
dialog.dispose();
});
JButton customButton = new JButton("Пользовательские цвета");
styleSecondaryButton(customButton);
customButton.addActionListener(e -> showCustomColorDialog());
JPanel buttonPanel = new JPanel(new GridLayout(1, 2, 10, 10));
buttonPanel.setBackground(currentBackgroundColor);
buttonPanel.add(customButton);
buttonPanel.add(applyButton);
dialog.add(contentPanel, BorderLayout.CENTER);
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.setVisible(true);
}
private void showCustomColorDialog() {
JDialog dialog = new JDialog(frame, "Пользовательские цвета", true);
dialog.setSize(400, 300);
dialog.setLocationRelativeTo(frame);
dialog.setLayout(new BorderLayout());
JPanel contentPanel = new JPanel(new GridLayout(3, 2, 10, 10));
contentPanel.setBackground(currentBackgroundColor);
contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
JLabel bgLabel = new JLabel("Фоновый цвет:");
bgLabel.setForeground(currentTextColor);
JButton bgButton = new JButton("Выбрать");
styleSecondaryButton(bgButton);
JLabel btnLabel = new JLabel("Цвет кнопок:");
btnLabel.setForeground(currentTextColor);
JButton btnColorButton = new JButton("Выбрать");
styleSecondaryButton(btnColorButton);
JLabel secLabel = new JLabel("Вторичный цвет:");
secLabel.setForeground(currentTextColor);
JButton secColorButton = new JButton("Выбрать");
styleSecondaryButton(secColorButton);
bgButton.addActionListener(e -> {
Color newColor = JColorChooser.showDialog(dialog, "Выберите фоновый цвет", currentBackgroundColor);
if (newColor != null) {
currentBackgroundColor = newColor;
bgButton.setBackground(newColor);
}
});
btnColorButton.addActionListener(e -> {
Color newColor = JColorChooser.showDialog(dialog, "Выберите цвет кнопок", currentButtonColor);
if (newColor != null) {
currentButtonColor = newColor;
btnColorButton.setBackground(newColor);
}
});
secColorButton.addActionListener(e -> {
Color newColor = JColorChooser.showDialog(dialog, "Выберите вторичный цвет", currentSecondaryColor);
if (newColor != null) {
currentSecondaryColor = newColor;
secColorButton.setBackground(newColor);
}
});
contentPanel.add(bgLabel);
contentPanel.add(bgButton);
contentPanel.add(btnLabel);
contentPanel.add(btnColorButton);
contentPanel.add(secLabel);
contentPanel.add(secColorButton);
JButton applyButton = new JButton("Применить");
styleOrangeButton(applyButton);
applyButton.addActionListener(e -> {
applyColors();
dialog.dispose();
});
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(currentBackgroundColor);
buttonPanel.add(applyButton);
dialog.add(contentPanel, BorderLayout.CENTER);
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.setVisible(true);
}
private void applyColors() {
frame.getContentPane().setBackground(currentBackgroundColor);
for (Component comp : frame.getContentPane().getComponents()) {
if (comp instanceof JPanel) {
updatePanelColors((JPanel) comp);
}
}
if (darkMode) {
currentTextColor = Color.WHITE;
} else {
currentTextColor = Color.BLACK;
}
frame.revalidate();
frame.repaint();
}
private void updatePanelColors(JPanel panel) {
panel.setBackground(currentBackgroundColor);
for (Component comp : panel.getComponents()) {
if (comp instanceof JPanel) {
updatePanelColors((JPanel) comp);
} else if (comp instanceof JButton) {
JButton button = (JButton) comp;
if (button.getText().equals("ИГРАТЬ")) {
styleOrangeButton(button);
} else {
styleSecondaryButton(button);
}
} else if (comp instanceof JTextPane) {
comp.setBackground(new Color(
Math.max(0, currentBackgroundColor.getRed() + 10),
Math.max(0, currentBackgroundColor.getGreen() + 10),
Math.max(0, currentBackgroundColor.getBlue() + 10)
));
comp.setForeground(currentTextColor);
} else if (comp instanceof JLabel) {
comp.setForeground(currentTextColor);
} else if (comp instanceof JScrollPane) {
comp.setBackground(currentBackgroundColor);
}
}
}
private void setupUI() {
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBackground(currentBackgroundColor);
mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
// Создаем верхнюю панель с названием и информацией об аккаунте
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.setBackground(currentBackgroundColor);
// Панель с названием и смайликом
JPanel titlePanel = new JPanel(new BorderLayout());
titlePanel.setBackground(currentBackgroundColor);
JLabel titleLabel = new JLabel("SGLauncher", SwingConstants.CENTER);
titleLabel.setFont(new Font("Arial", Font.BOLD, 28));
titleLabel.setForeground(currentTextColor);
titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0));
JLabel cuteImage = new JLabel("", SwingConstants.CENTER);
cuteImage.setFont(new Font("Arial", Font.PLAIN, 48));
cuteImage.setForeground(currentButtonColor);
titlePanel.add(titleLabel, BorderLayout.NORTH);
titlePanel.add(cuteImage, BorderLayout.CENTER);
// Панель аккаунта (в стиле Steam - в правом верхнем углу)
JPanel accountInfoPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 5));
accountInfoPanel.setBackground(currentBackgroundColor);
if (isLoggedIn && currentAccount != null) {
// Аватарка (маленькая, как в Steam)
JLabel avatarLabel;
if (!currentAccount.getAvatarPath().isEmpty()) {
try {
Image img = ImageIO.read(new File(currentAccount.getAvatarPath()));
img = img.getScaledInstance(32, 32, Image.SCALE_SMOOTH);
avatarLabel = new JLabel(new ImageIcon(img));
} catch (Exception ex) {
avatarLabel = new JLabel(guestIcon);
}
} else {
avatarLabel = new JLabel(guestIcon);
}
// Имя пользователя (компактное)
JLabel usernameLabel = new JLabel(currentAccount.getUsername());
usernameLabel.setFont(new Font("Arial", Font.BOLD, 12));
usernameLabel.setForeground(currentTextColor);
accountInfoPanel.add(avatarLabel);
accountInfoPanel.add(usernameLabel);
} else {
// Отображение для гостя
JLabel guestLabel = new JLabel(guestIcon);
JLabel guestText = new JLabel("Гость");
guestText.setFont(new Font("Arial", Font.PLAIN, 12));
guestText.setForeground(currentTextColor);
accountInfoPanel.add(guestLabel);
accountInfoPanel.add(guestText);
}
topPanel.add(titlePanel, BorderLayout.CENTER);
topPanel.add(accountInfoPanel, BorderLayout.EAST);
listModel = new DefaultListModel<>();
for (Game game : games) {
listModel.addElement(game);
}
gameList = new JList<>(listModel);
gameList.setCellRenderer(new GameListRenderer());
gameList.setBackground(new Color(
Math.max(0, currentBackgroundColor.getRed() + 20),
Math.max(0, currentBackgroundColor.getGreen() + 20),
Math.max(0, currentBackgroundColor.getBlue() + 20)
));
gameList.setForeground(currentTextColor);
gameList.setSelectionBackground(currentButtonColor);
gameList.setBorder(BorderFactory.createLineBorder(new Color(
Math.max(0, currentBackgroundColor.getRed() + 40),
Math.max(0, currentBackgroundColor.getGreen() + 40),
Math.max(0, currentBackgroundColor.getBlue() + 40)
), 2));
gameList.addListSelectionListener(e -> updateDescription());
JScrollPane listScrollPane = new JScrollPane(gameList);
listScrollPane.setBorder(BorderFactory.createEmptyBorder());
listScrollPane.setPreferredSize(new Dimension(300, 400));
descriptionPane = new JTextPane();
descriptionPane.setContentType("text/html");
descriptionPane.setEditable(false);
descriptionPane.setBackground(new Color(
Math.max(0, currentBackgroundColor.getRed() + 10),
Math.max(0, currentBackgroundColor.getGreen() + 10),
Math.max(0, currentBackgroundColor.getBlue() + 10)
));
descriptionPane.setForeground(currentTextColor);
descriptionPane.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15));
descriptionPane.setText("<html><body style='color:" + (darkMode ? "white" : "black") + "; font-family:Arial;'>"
+ "<h2 style='color:" + String.format("#%02x%02x%02x",
currentButtonColor.getRed(),
currentButtonColor.getGreen(),
currentButtonColor.getBlue()) + "'>Выберите игру</h2>"
+ "<p>Данный лаунчер подходит для запуска игр exe и jar</p>"
+ "<p>Текущая версия: v14.1 Alpha open test</p>"
+ "<p>Возможности:</p>"
+ "<ul>"
+ "<li>Запуск обычных игр</li>"
+ "<li>Запуск jar игр</li>"
+ "<li>Запуск Steam игр без Steam</li>"
+ "<li>Добавление своих игр</li>"
+ "</ul></body></html>");
JScrollPane descScrollPane = new JScrollPane(descriptionPane);
descScrollPane.setBorder(BorderFactory.createLineBorder(new Color(
Math.max(0, currentBackgroundColor.getRed() + 40),
Math.max(0, currentBackgroundColor.getGreen() + 40),
Math.max(0, currentBackgroundColor.getBlue() + 40)
), 2));
JPanel contentPanel = new JPanel(new GridBagLayout());
contentPanel.setBackground(currentBackgroundColor);
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
// Левая панель (список игр)
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBackground(currentBackgroundColor);
leftPanel.add(listScrollPane, BorderLayout.CENTER);
// Центральная панель (описание)
JPanel centerPanel = new JPanel(new BorderLayout());
centerPanel.setBackground(currentBackgroundColor);
centerPanel.add(descScrollPane, BorderLayout.CENTER);
// Распределение пространства
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 0.35;
contentPanel.add(leftPanel, gbc);
gbc.gridx = 1;
gbc.weightx = 0.65;
contentPanel.add(centerPanel, gbc);
buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10));
buttonPanel.setBackground(currentBackgroundColor);
JButton playButton = new JButton("ИГРАТЬ");
styleOrangeButton(playButton);
playButton.addActionListener(e -> launchSelectedGame());
JButton downloadButton = new JButton("СКАЧАТЬ ИЗ ОБЛАКА");
styleSecondaryButton(downloadButton);
downloadButton.addActionListener(e -> {
try {
Desktop.getDesktop().browse(new URI("https://sites.google.com/view/sglauncher/%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F-%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0"));
} catch (Exception ex) {
showError("Не удалось открыть сайт: " + ex.getMessage());
}
});
JButton addButton = new JButton("+ ДОБАВИТЬ ИГРУ");
styleSecondaryButton(addButton);
addButton.addActionListener(e -> showAddGameDialog());
buttonPanel.add(playButton);
buttonPanel.add(downloadButton);
buttonPanel.add(addButton);
mainPanel.add(topPanel, BorderLayout.NORTH);
mainPanel.add(contentPanel, BorderLayout.CENTER);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
frame.setContentPane(mainPanel);
frame.setVisible(true);
}
private void updateDescription() {
Game selectedGame = gameList.getSelectedValue();
if (selectedGame != null) {
String steamInfo = selectedGame.isSteamGame()
? "<p><b>Steam ID:</b> " + selectedGame.getSteamAppId() + "</p>"
: "";
String lastPlayed = selectedGame.getLastPlayed() > 0
? "<p><b>Последний запуск:</b> " + new java.util.Date(selectedGame.getLastPlayed()) + "</p>"
: "<p><b>Последний запуск:</b> Никогда</p>";
String playCount = "<p><b>Количество запусков:</b> " + selectedGame.getPlayCount() + "</p>";
descriptionPane.setText("<html><body style='color:" + (darkMode ? "white" : "black") + "; font-family:Arial;'>"
+ "<h2 style='color:" + String.format("#%02x%02x%02x",
currentButtonColor.getRed(),
currentButtonColor.getGreen(),
currentButtonColor.getBlue()) + "'>" + selectedGame.getName() + "</h2>"
+ "<p><b>Версия:</b> " + selectedGame.getVersion() + "</p>"
+ "<p><b>Путь:</b> " + selectedGame.getPathToExecutable() + "</p>"
+ steamInfo
+ lastPlayed
+ playCount
+ "</body></html>");
}
}
private void styleSecondaryButton(JButton button) {
button.setFont(new Font("Arial", Font.BOLD, 14));
button.setBackground(currentSecondaryColor);
button.setForeground(currentTextColor);
button.setFocusPainted(false);
button.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(
Math.min(255, currentSecondaryColor.getRed() + 30),
Math.min(255, currentSecondaryColor.getGreen() + 30),
Math.min(255, currentSecondaryColor.getBlue() + 30)
), 2),
BorderFactory.createEmptyBorder(8, 20, 8, 20)
));
button.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
button.setBackground(new Color(
Math.min(255, currentSecondaryColor.getRed() + 20),
Math.min(255, currentSecondaryColor.getGreen() + 20),
Math.min(255, currentSecondaryColor.getBlue() + 20)
));
}
public void mouseExited(MouseEvent e) {
button.setBackground(currentSecondaryColor);
}
});
}
private void styleOrangeButton(JButton button) {
button.setFont(new Font("Arial", Font.BOLD, 16));
button.setBackground(currentButtonColor);
button.setForeground(Color.BLACK);
button.setFocusPainted(false);
button.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(new Color(
Math.max(0, currentButtonColor.getRed() - 55),
Math.max(0, currentButtonColor.getGreen() - 30),
Math.max(0, currentButtonColor.getBlue() - 0)
), 2),
BorderFactory.createEmptyBorder(10, 25, 10, 25)
));
button.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
button.setBackground(new Color(
Math.min(255, currentButtonColor.getRed() + 20),
Math.min(255, currentButtonColor.getGreen() + 20),
Math.min(255, currentButtonColor.getBlue() + 20)
));
}
public void mouseExited(MouseEvent e) {
button.setBackground(currentButtonColor);
}
});
}
private void showAddGameDialog() {
JDialog dialog = new JDialog(frame, "Добавить игру", true);
dialog.setSize(600, 400);
dialog.setLocationRelativeTo(frame);
dialog.setLayout(new BorderLayout());
JPanel contentPanel = new JPanel(new GridBagLayout());
contentPanel.setBackground(new Color(
Math.max(0, currentBackgroundColor.getRed() + 10),
Math.max(0, currentBackgroundColor.getGreen() + 10),
Math.max(0, currentBackgroundColor.getBlue() + 10)
));
contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 0;
JLabel nameLabel = new JLabel("Название игры:");
nameLabel.setForeground(currentTextColor);
contentPanel.add(nameLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JTextField nameField = new JTextField();
contentPanel.add(nameField, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 1;
JLabel versionLabel = new JLabel("Версия:");
versionLabel.setForeground(currentTextColor);
contentPanel.add(versionLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JTextField versionField = new JTextField();
contentPanel.add(versionField, gbc);
gbc.gridx = 0;
gbc.gridy = 2;
gbc.gridwidth = 1;
JLabel pathLabel = new JLabel("Путь к файлу:");
pathLabel.setForeground(currentTextColor);
contentPanel.add(pathLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 1;
JTextField pathField = new JTextField();
contentPanel.add(pathField, gbc);
gbc.gridx = 2;
gbc.gridwidth = 1;
JButton browseButton = new JButton("Обзор");
styleSecondaryButton(browseButton);
browseButton.addActionListener(e -> {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Выберите исполняемый файл игры");
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() ||
f.getName().toLowerCase().endsWith(".exe") ||
f.getName().toLowerCase().endsWith(".jar");
}
@Override
public String getDescription() {
return "Исполняемые файлы (*.exe, *.jar)";
}
});
if (fileChooser.showOpenDialog(dialog) == JFileChooser.APPROVE_OPTION) {
pathField.setText(fileChooser.getSelectedFile().getAbsolutePath());
}
});
contentPanel.add(browseButton, gbc);
gbc.gridx = 0;
gbc.gridy = 3;
gbc.gridwidth = 1;
JCheckBox steamCheckBox = new JCheckBox("Steam игра");
steamCheckBox.setForeground(currentTextColor);
steamCheckBox.setBackground(new Color(
Math.max(0, currentBackgroundColor.getRed() + 10),
Math.max(0, currentBackgroundColor.getGreen() + 10),
Math.max(0, currentBackgroundColor.getBlue() + 10)
));
contentPanel.add(steamCheckBox, gbc);
gbc.gridx = 0;
gbc.gridy = 4;
JLabel steamIdLabel = new JLabel("Steam App ID:");
steamIdLabel.setForeground(currentTextColor);
contentPanel.add(steamIdLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JTextField steamIdField = new JTextField();
steamIdField.setEnabled(false);
contentPanel.add(steamIdField, gbc);
steamCheckBox.addActionListener(e -> {
steamIdField.setEnabled(steamCheckBox.isSelected());
});
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
buttonPanel.setBackground(new Color(
Math.max(0, currentBackgroundColor.getRed() + 10),
Math.max(0, currentBackgroundColor.getGreen() + 10),
Math.max(0, currentBackgroundColor.getBlue() + 10)
));
JButton addButton = new JButton("Добавить");
styleOrangeButton(addButton);
addButton.addActionListener(e -> {
String name = nameField.getText();
String version = versionField.getText();
String path = pathField.getText();
if (!name.isEmpty() && !path.isEmpty()) {
boolean isSteamGame = steamCheckBox.isSelected();
String steamAppId = steamIdField.getText();
if (isSteamGame && steamAppId.isEmpty()) {
showError("Для Steam игры необходимо указать App ID!");
return;
}
Game newGame = isSteamGame
? new Game(name, version.isEmpty() ? "1.0" : version, path, "", true, steamAppId)
: new Game(name, version.isEmpty() ? "1.0" : version, path, "https://sglauncher.ulcraft.com");
listModel.addElement(newGame);
saveGames();
showInfo("Игра " + name + " успешно добавлена");
dialog.dispose();
} else {
showError("Заполните название и путь к игре!");
}
});
buttonPanel.add(addButton);
dialog.add(contentPanel, BorderLayout.CENTER);
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.setVisible(true);
}
private void launchSelectedGame() {
Game selectedGame = gameList.getSelectedValue();
if (selectedGame == null) {
showWarning("Выберите игру!");
return;
}
try {
selectedGame.incrementPlayCount();
selectedGame.setLastPlayed(System.currentTimeMillis());
saveGames();
if (selectedGame.isSteamGame()) {
launchSteamGameWithoutSteam(selectedGame);
} else {
launchRegularGame(selectedGame);
}
showInfo("Игра " + selectedGame.getName() + " запускается...");
} catch (Exception e) {
showError("Ошибка запуска: " + e.getMessage());
}
}
private void launchRegularGame(Game game) throws IOException {
File gameFile = new File(game.getPathToExecutable());
if (!gameFile.exists()) {
throw new IOException("Файл игры не найден: " + game.getPathToExecutable());
}
String command;
if (game.getPathToExecutable().endsWith(".jar")) {
command = "java -jar \"" + game.getPathToExecutable() + "\"";
} else {
command = "\"" + game.getPathToExecutable() + "\"";
}
ProcessBuilder pb = new ProcessBuilder();
if (game.getPathToExecutable().endsWith(".jar")) {
pb.command("java", "-jar", game.getPathToExecutable());
} else {
pb.command(game.getPathToExecutable());
}
pb.start();
}
private void launchSteamGameWithoutSteam(Game game) throws IOException {
File gameFile = new File(game.getPathToExecutable());
if (!gameFile.exists()) {
throw new IOException("Файл игры не найден: " + game.getPathToExecutable());
}
// Создаем временную папку для эмуляции Steam
File steamEmuDir = Files.createTempDirectory("steam_emu").toFile();
File steamAppIdFile = new File(steamEmuDir, "steam_appid.txt");
File steamSettingsFile = new File(steamEmuDir, "steam_settings.ini");
// Записываем необходимые файлы
try (FileWriter appIdWriter = new FileWriter(steamAppIdFile);
FileWriter settingsWriter = new FileWriter(steamSettingsFile)) {
appIdWriter.write(game.getSteamAppId());
settingsWriter.write("[Settings]\n");
settingsWriter.write("AppId=" + game.getSteamAppId() + "\n");
settingsWriter.write("RunCallbacks=1\n");
settingsWriter.write("SteamClientDll=\n");
settingsWriter.write("DisableOverlay=1\n");
settingsWriter.write("DisableNetworking=1\n");
settingsWriter.write("DisableUserStats=1\n");
settingsWriter.write("DisableAchievements=1\n");
settingsWriter.write("Offline=1\n");
settingsWriter.write("UnlockAllDLC=1\n");
settingsWriter.write("BypassLicenseCheck=1\n");
}
// Создаем bat-файл для запуска с нужными переменными окружения
String batContent = "@echo off\n"
+ "set STEAM_COMPAT_DATA_PATH=\"" + steamEmuDir.getAbsolutePath() + "\"\n"
+ "set STEAM_COMPAT_CLIENT_INSTALL_PATH=\"" + steamEmuDir.getAbsolutePath() + "\"\n"
+ "set SteamAppId=" + game.getSteamAppId() + "\n"
+ "set SteamGameId=" + game.getSteamAppId() + "\n"
+ "set SteamClientLaunch=1\n"
+ "set SteamEnv=1\n"
+ "set SteamOverlayGameId=" + game.getSteamAppId() + "\n"
+ "set SteamNoOverlayUI=1\n"
+ "start \"\" \"" + game.getPathToExecutable() + "\"\n"
+ "exit";
File tempBat = File.createTempFile("steam_launch", ".bat");
try (FileWriter writer = new FileWriter(tempBat)) {
writer.write(batContent);
}
// Запускаем игру
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "start", "\"\"", "\"" + tempBat.getAbsolutePath() + "\"");
pb.start();
// Удаляем временные файлы после завершения
tempBat.deleteOnExit();
steamEmuDir.deleteOnExit();
steamAppIdFile.deleteOnExit();
steamSettingsFile.deleteOnExit();
}
private void checkForUpdates() {
try {
showInfo("Проверка обновлений...\nВаша версия лаунчера актуальна: v14.1 Alpha open test");
} catch (Exception e) {
showError("Ошибка при проверке обновлений: " + e.getMessage());
}
}
private void showStatsDialog() {
JDialog dialog = new JDialog(frame, "Статистика", true);
dialog.setSize(400, 300);
dialog.setLocationRelativeTo(frame);
dialog.setLayout(new BorderLayout());
JPanel contentPanel = new JPanel(new GridLayout(0, 1, 10, 10));
contentPanel.setBackground(currentBackgroundColor);
contentPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
long daysUsed = (System.currentTimeMillis() - firstLaunchTime) / (1000 * 60 * 60 * 24);
long hoursUsed = (System.currentTimeMillis() - firstLaunchTime) / (1000 * 60 * 60);
JLabel totalLaunchesLabel = new JLabel("Всего запусков лаунчера: " + totalLaunches);
JLabel firstLaunchLabel = new JLabel("Первый запуск: " + new java.util.Date(firstLaunchTime));
JLabel usageTimeLabel = new JLabel("Дней с первого запуска: " + daysUsed);
JLabel gamesCountLabel = new JLabel("Количество игр в лаунчере: " + listModel.size());
totalLaunchesLabel.setForeground(currentTextColor);
firstLaunchLabel.setForeground(currentTextColor);
usageTimeLabel.setForeground(currentTextColor);
gamesCountLabel.setForeground(currentTextColor);
contentPanel.add(totalLaunchesLabel);
contentPanel.add(firstLaunchLabel);
contentPanel.add(usageTimeLabel);
contentPanel.add(gamesCountLabel);
JButton closeButton = new JButton("Закрыть");
styleOrangeButton(closeButton);
closeButton.addActionListener(e -> dialog.dispose());
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(currentBackgroundColor);
buttonPanel.add(closeButton);
dialog.add(contentPanel, BorderLayout.CENTER);
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.setVisible(true);
}
private void exportGames() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Экспорт списка игр");
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setSelectedFile(new File("games_export.json"));
if (fileChooser.showSaveDialog(frame) == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
try (PrintWriter writer = new PrintWriter(file)) {
for (int i = 0; i < listModel.size(); i++) {
Game game = listModel.get(i);
writer.println(game.getName() + "|" + game.getVersion() + "|" +
game.getPathToExecutable() + "|" + game.isSteamGame() + "|" +
game.getSteamAppId());
}
showInfo("Список игр успешно экспортирован в " + file.getAbsolutePath());
} catch (IOException e) {
showError("Ошибка при экспорте списка игр: " + e.getMessage());
}
}
}
private void importGames() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Импорт списка игр");
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (fileChooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
int importedCount = 0;
while ((line = reader.readLine()) != null) {
String[] parts = line.split("\\|");
if (parts.length >= 5) {
boolean isSteam = Boolean.parseBoolean(parts[3]);
Game game = isSteam
? new Game(parts[0], parts[1], parts[2], "", true, parts[4])
: new Game(parts[0], parts[1], parts[2], "https://sglauncher.ulcraft.com");
listModel.addElement(game);
importedCount++;
}
}
saveGames();
showInfo("Успешно импортировано " + importedCount + " игр");
} catch (IOException e) {
showError("Ошибка при импорте списка игр: " + e.getMessage());
}
}
}
private void showError(String message) {
JOptionPane.showMessageDialog(frame, message, "Ошибка", JOptionPane.ERROR_MESSAGE);
}
private void showWarning(String message) {
JOptionPane.showMessageDialog(frame, message, "Предупреждение", JOptionPane.WARNING_MESSAGE);
}
private void showInfo(String message) {
JOptionPane.showMessageDialog(frame, message, "Информация", JOptionPane.INFORMATION_MESSAGE);
}
// ========== АККАУНТ СИСТЕМА ==========
private void showCompactLoginDialog() {
JDialog dialog = new JDialog(frame, "Вход", true);
dialog.setSize(300, 200);
dialog.setLocationRelativeTo(frame);
dialog.setLayout(new GridBagLayout());
JPanel contentPanel = new JPanel(new GridBagLayout());
contentPanel.setBackground(currentBackgroundColor);
contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 0;
JLabel usernameLabel = new JLabel("Имя пользователя:");
usernameLabel.setForeground(currentTextColor);
contentPanel.add(usernameLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JTextField usernameField = new JTextField(15);
contentPanel.add(usernameField, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 1;
JLabel passwordLabel = new JLabel("Пароль:");
passwordLabel.setForeground(currentTextColor);
contentPanel.add(passwordLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JPasswordField passwordField = new JPasswordField(15);
contentPanel.add(passwordField, gbc);
gbc.gridx = 1;
gbc.gridy = 2;
gbc.gridwidth = 1;
JCheckBox rememberMe = new JCheckBox("Запомнить меня");
rememberMe.setBackground(currentBackgroundColor);
rememberMe.setForeground(currentTextColor);
contentPanel.add(rememberMe, gbc);
JButton loginButton = new JButton("Войти");
styleOrangeButton(loginButton);
loginButton.addActionListener(e -> {
String username = usernameField.getText();
String password = new String(passwordField.getPassword());
if (username.isEmpty() || password.isEmpty()) {
showError("Заполните все поля!");
return;
}
try {
Account account = authenticate(username, password);
if (account != null) {
currentAccount = account;
currentAccount.setRememberMe(rememberMe.isSelected());
isLoggedIn = true;
updateAccountPanel();
updateMenuVisibility();
saveLastAccount();
dialog.dispose();
showInfo("Вход выполнен успешно!");
} else {
showError("Неверное имя пользователя или пароль");
}
} catch (Exception ex) {
showError("Ошибка входа: " + ex.getMessage());
}
});
gbc.gridx = 1;
gbc.gridy = 3;
gbc.gridwidth = 1;
contentPanel.add(loginButton, gbc);
dialog.add(contentPanel);
dialog.setVisible(true);
}
private void showCompactRegisterDialog() {
JDialog dialog = new JDialog(frame, "Регистрация", true);
dialog.setSize(300, 250);
dialog.setLocationRelativeTo(frame);
dialog.setLayout(new GridBagLayout());
JPanel contentPanel = new JPanel(new GridBagLayout());
contentPanel.setBackground(currentBackgroundColor);
contentPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(5, 5, 5, 5);
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 0;
JLabel usernameLabel = new JLabel("Имя пользователя:");
usernameLabel.setForeground(currentTextColor);
contentPanel.add(usernameLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JTextField usernameField = new JTextField(15);
contentPanel.add(usernameField, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 1;
JLabel emailLabel = new JLabel("Email:");
emailLabel.setForeground(currentTextColor);
contentPanel.add(emailLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JTextField emailField = new JTextField(15);
contentPanel.add(emailField, gbc);
gbc.gridx = 0;
gbc.gridy = 2;
gbc.gridwidth = 1;
JLabel passwordLabel = new JLabel("Пароль:");
passwordLabel.setForeground(currentTextColor);
contentPanel.add(passwordLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JPasswordField passwordField = new JPasswordField(15);
contentPanel.add(passwordField, gbc);
gbc.gridx = 0;
gbc.gridy = 3;
gbc.gridwidth = 1;
JLabel confirmLabel = new JLabel("Подтвердите:");
confirmLabel.setForeground(currentTextColor);
contentPanel.add(confirmLabel, gbc);
gbc.gridx = 1;
gbc.gridwidth = 2;
JPasswordField confirmField = new JPasswordField(15);
contentPanel.add(confirmField, gbc);
JButton registerButton = new JButton("Зарегистрироваться");
styleOrangeButton(registerButton);
registerButton.addActionListener(e -> {
String username = usernameField.getText();
String email = emailField.getText();
String password = new String(passwordField.getPassword());
String confirm = new String(confirmField.getPassword());
if (!password.equals(confirm)) {
showError("Пароли не совпадают!");
return;
}
try {
registerAccount(username, email, password);
showInfo("Регистрация прошла успешно!");
dialog.dispose();
} catch (Exception ex) {
showError("Ошибка регистрации: " + ex.getMessage());
}
});
gbc.gridx = 1;
gbc.gridy = 4;
gbc.gridwidth = 1;
contentPanel.add(registerButton, gbc);
dialog.add(contentPanel);
dialog.setVisible(true);
}
@SuppressWarnings("unchecked")
private Account authenticate(String username, String password) throws Exception {
File accountsFile = new File(ACCOUNTS_FILE);
if (!accountsFile.exists()) return null;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(ACCOUNTS_FILE))) {
List<Account> accounts = (List<Account>) ois.readObject();
for (Account acc : accounts) {
if (acc.getUsername().equals(username) && acc.verifyPassword(password)) {
return acc;
}
}
}
return null;
}
@SuppressWarnings("unchecked")
private void registerAccount(String username, String email, String password) throws Exception {
File accountsFile = new File(ACCOUNTS_FILE);
List<Account> accounts = new ArrayList<>();
if (accountsFile.exists()) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(ACCOUNTS_FILE))) {
accounts = (List<Account>) ois.readObject();
}
}
// Проверяем, есть ли уже такой пользователь
for (Account acc : accounts) {
if (acc.getUsername().equals(username)) {
throw new Exception("Пользователь уже существует");
}
}
Account newAccount = new Account(username, email, password);
accounts.add(newAccount);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(ACCOUNTS_FILE))) {
oos.writeObject(accounts);
}
currentAccount = newAccount;
isLoggedIn = true;
updateAccountPanel();
updateMenuVisibility();
}
private void logout() {
currentAccount = null;
isLoggedIn = false;
updateAccountPanel();
updateMenuVisibility();
showInfo("Вы вышли из аккаунта");
}
private void setAvatarIcon(String icon) {
if (!isLoggedIn) {
showError("Войдите в аккаунт!");
return;
}
try {
// Создаем изображение с иконкой
BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.setColor(currentButtonColor);
g2d.fillOval(0, 0, 100, 100);
g2d.setColor(Color.BLACK);
g2d.setFont(new Font("Arial", Font.BOLD, 60));
FontMetrics fm = g2d.getFontMetrics();
int x = (100 - fm.stringWidth(icon)) / 2;
int y = ((100 - fm.getHeight()) / 2) + fm.getAscent();
g2d.drawString(icon, x, y);
g2d.dispose();
// Сохраняем изображение
File avatarFile = new File("avatars/" + currentAccount.getUsername() + "_icon.png");
avatarFile.getParentFile().mkdirs();
ImageIO.write(image, "png", avatarFile);
currentAccount.setAvatarPath(avatarFile.getAbsolutePath());
updateAccountPanel();
showInfo("Аватарка изменена на иконку: " + icon);
} catch (Exception ex) {
showError("Ошибка создания аватарки: " + ex.getMessage());
}
}
private void changeAvatar() {
if (!isLoggedIn) {
showError("Войдите в аккаунт!");
return;
}
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Выберите аватарку");
fileChooser.setAcceptAllFileFilterUsed(false);
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Изображения", "jpg", "jpeg", "png", "gif"));
if (fileChooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
try {
// Сохраняем изображение в папке с лаунчером
File avatarFile = new File("avatars/" + currentAccount.getUsername() + ".png");
avatarFile.getParentFile().mkdirs();
BufferedImage image = ImageIO.read(selectedFile);
ImageIO.write(image, "png", avatarFile);
currentAccount.setAvatarPath(avatarFile.getAbsolutePath());
updateAccountPanel();
showInfo("Аватарка изменена!");
} catch (Exception ex) {
showError("Ошибка загрузки аватарки: " + ex.getMessage());
}
}
}
private void changeWallpaper() {
if (!isLoggedIn) {
showError("Войдите в аккаунт!");
return;
}
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Выберите обои");
fileChooser.setAcceptAllFileFilterUsed(false);
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Изображения", "jpg", "jpeg", "png", "gif"));
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Видео", "mp4", "mov"));
if (fileChooser.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) {
File selectedFile = fileChooser.getSelectedFile();
String ext = selectedFile.getName().substring(selectedFile.getName().lastIndexOf(".") + 1).toLowerCase();
try {
// Сохраняем в папке с лаунчером
File wallpaperFile = new File("wallpapers/" + currentAccount.getUsername() + "." + ext);
wallpaperFile.getParentFile().mkdirs();
Files.copy(selectedFile.toPath(), wallpaperFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
currentAccount.setWallpaperPath(wallpaperFile.getAbsolutePath());
updateAccountPanel();
showInfo("Обои изменены!");
} catch (Exception ex) {
showError("Ошибка загрузки обоев: " + ex.getMessage());
}
}
}
private void updateMenuVisibility() {
JMenuBar menuBar = frame.getJMenuBar();
for (int i = 0; i < menuBar.getMenuCount(); i++) {
JMenu menu = menuBar.getMenu(i);
if (menu.getText().equals("Аккаунт")) {
for (int j = 0; j < menu.getItemCount(); j++) {
JMenuItem item = menu.getItem(j);
if (item != null) {
if (item.getText().equals("Войти") || item.getText().equals("Зарегистрироваться")) {
item.setVisible(!isLoggedIn);
} else if (item.getText().equals("Выйти") || item.getText().startsWith("Изменить")) {
item.setVisible(isLoggedIn);
} else if (item.getText().equals("Изменить аватарку")) {
// Это меню, нужно обработать его элементы
JMenu subMenu = (JMenu) item;
for (int k = 0; k < subMenu.getItemCount(); k++) {
subMenu.getItem(k).setVisible(isLoggedIn);
}
}
}
}
}
}
}
private void updateAccountPanel() {
// Обновляем информацию об аккаунте в верхней панели
JPanel topPanel = (JPanel) frame.getContentPane().getComponent(0);
JPanel accountInfoPanel = (JPanel) topPanel.getComponent(1); // accountInfoPanel находится в EAST
accountInfoPanel.removeAll();
accountInfoPanel.setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 5));
accountInfoPanel.setBackground(currentBackgroundColor);
if (isLoggedIn && currentAccount != null) {
// Аватарка (маленькая, как в Steam)
JLabel avatarLabel;
if (!currentAccount.getAvatarPath().isEmpty()) {
try {
Image img = ImageIO.read(new File(currentAccount.getAvatarPath()));
img = img.getScaledInstance(32, 32, Image.SCALE_SMOOTH);
avatarLabel = new JLabel(new ImageIcon(img));
} catch (Exception ex) {
avatarLabel = new JLabel(guestIcon);
}
} else {
avatarLabel = new JLabel(guestIcon);
}
// Имя пользователя (компактное)
JLabel usernameLabel = new JLabel(currentAccount.getUsername());
usernameLabel.setFont(new Font("Arial", Font.BOLD, 12));
usernameLabel.setForeground(currentTextColor);
accountInfoPanel.add(avatarLabel);
accountInfoPanel.add(usernameLabel);
} else {
// Отображение для гостя
JLabel guestLabel = new JLabel(guestIcon);
JLabel guestText = new JLabel("Гость");
guestText.setFont(new Font("Arial", Font.PLAIN, 12));
guestText.setForeground(currentTextColor);
accountInfoPanel.add(guestLabel);
accountInfoPanel.add(guestText);
}
accountInfoPanel.revalidate();
accountInfoPanel.repaint();
}
private static class GameListRenderer extends DefaultListCellRenderer {
@Override
public Component getListCellRendererComponent(
JList<?> list, Object value, int index,
boolean isSelected, boolean cellHasFocus
) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
Game game = (Game) value;
String steamIcon = game.isSteamGame() ? " <font color='#66C0F4'>[Steam]</font>" : "";
String playCount = game.getPlayCount() > 0 ? " <font color='#AAAAAA'>(" + game.getPlayCount() + ")</font>" : "";
setText("<html><b>" + game.getName() + "</b> <font color='#AAAAAA'>v" + game.getVersion() + steamIcon + playCount + "</font></html>");
setBackground(isSelected ? getBackground() : new Color(50, 50, 50));
setForeground(isSelected ? Color.BLACK : Color.WHITE);
setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
return this;
}
}
private static class ColorTemplate {
private String name;
private Color background;
private Color button;
private Color secondary;
public ColorTemplate(String name, Color background, Color button, Color secondary) {
this.name = name;
this.background = background;
this.button = button;
this.secondary = secondary;
}
public String getName() { return name; }
public Color getBackground() { return background; }
public Color getButton() { return button; }
public Color getSecondary() { return secondary; }
}
private static class RoundedBorder implements Border {
private int radius;
public RoundedBorder(int radius) {
this.radius = radius;
}
public Insets getBorderInsets(Component c) {
return new Insets(this.radius+1, this.radius+1, this.radius+2, this.radius);
}
public boolean isBorderOpaque() {
return true;
}
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
g.drawRoundRect(x, y, width-1, height-1, radius, radius);
}
}
}
Сейчас версия 13.2
Вышла : 22.07.2025