package im.expensive.another.screen;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import im.expensive.ui.mainmenu.MainScreen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Session;
import net.minecraft.util.text.StringTextComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.*;
public class AccountManagement extends Screen {
private static final Logger LOGGER = LogManager.getLogger();
private static final long DOUBLE_CLICK_INTERVAL = 250;
private static final int ACCOUNT_LIST_START_Y = 45;
private static final int ACCOUNT_LIST_ENTRY_HEIGHT = 20;
private static final int MAX_LIST_HEIGHT = 400;
private static final int LIST_WIDTH = 400;
private static final int BUTTON_WIDTH = 100;
private static final int BUTTON_HEIGHT = 20;
private static final int TEXT_FIELD_HEIGHT = 20;
private static final int COLOR_SELECTED = new Color(124, 124, 125, 255).getRGB();
private static final int COLOR_BACKGROUND = new Color(0, 0, 0, 255).getRGB();
private static final int COLOR_HIGHLIGHT = new Color(0, 0, 0, 100).getRGB();
private static final int COLOR_WHITE = 0xFFFFFF;
private static final int COLOR_GREEN = 0x00FF00;
private static final int COLOR_HINT = 0xCCCCCC;
private static final ResourceLocation STEVE_SKIN = new ResourceLocation("textures/entity/steve.png");
private static final String ACCOUNTS_FILE_PATH = "Clouse/files/accounts.clouse";
private long lastClickTime = 0;
private boolean isScrolling = false;
private int lastMouseY = 0;
private int scrollOffset = 0;
private Button loginButton;
private Button deleteButton;
private static List<Account> filteredAccounts = new ArrayList<>();
private static final List<Account> accounts = new ArrayList<>();
private Account selectedAccount = null;
private static TextFieldWidget searchField;
private TextFieldWidget fieldWidget;
private int mouseY;
public AccountManagement() {
super(new StringTextComponent("Активный аккаунт"));
loadAccounts();
}
private static class Account {
String username;
Account(String username) {
this.username = username;
}
}
@Override
public void init() {
int centerX = this.width / 2;
int centerY = this.height / 2;
int listX = centerX - LIST_WIDTH / 2;
int searchFieldX = centerX - 100;
// Поисковое текстовое поле для фильтрации аккаунтов
searchField = new TextFieldWidget(font, searchFieldX, 20, 200, TEXT_FIELD_HEIGHT, new StringTextComponent(""));
searchField.setResponder(this::filterAccounts);
this.children.add(searchField);
//Текст-заполнитель для searchField
searchField.setSuggestion("");
int buttonY = this.height - 35;
int buttonSpacing = BUTTON_WIDTH + 5;
// Добавить кнопку для действий
loginButton = addButton(new Button(centerX - 2 * buttonSpacing , buttonY, BUTTON_WIDTH, BUTTON_HEIGHT, new StringTextComponent("Войти"), button -> loginSelectedAccount()));
deleteButton = addButton(new Button(centerX - buttonSpacing, buttonY, BUTTON_WIDTH, BUTTON_HEIGHT, new StringTextComponent("Удалить"), button -> deleteSelectedAccount()));
addButton(new Button(centerX, buttonY, BUTTON_WIDTH, BUTTON_HEIGHT, new StringTextComponent("Добавить аккаунт"), button -> Minecraft.getInstance().displayGuiScreen(new AddAccountScreen())));
addButton(new Button(centerX + buttonSpacing, buttonY, BUTTON_WIDTH, BUTTON_HEIGHT, new StringTextComponent("Выйти"), button -> Minecraft.getInstance().displayGuiScreen(new MainScreen())));
// Текстовое поле для ввода имени пользователя (скрыто по умолчанию)
fieldWidget = new TextFieldWidget(this.font, centerX - 150, 100, 300, TEXT_FIELD_HEIGHT, new StringTextComponent("Никнейм"));
fieldWidget.setVisible(false);
fieldWidget.setMaxStringLength(16);
this.children.add(fieldWidget);
// Изначально показывать все аккаунты
filteredAccounts = new ArrayList<>(accounts);
}
public void addAccount(String username) {
if (!isValidUsername(username)) {
LOGGER.warn("Попытка с недопустимым именем пользователя: " + username);
return;
}
for (Account account : accounts) {
if (account.username.equalsIgnoreCase(username)) {
LOGGER.warn("Попытка с дублирующимся именем пользователя: " + username);
return; // предотвратить добавление дубликатов имен пользователей
}
}
accounts.add(new Account(username));
saveAccounts();
}
@Override
public void render(MatrixStack matrixStack, int mouseX, int mouseY, float partialTicks) {
this.renderBackground(matrixStack);
super.render(matrixStack, mouseX, mouseY, partialTicks);
loginButton.active = selectedAccount != null;
deleteButton.active = selectedAccount != null;
// Обновить подсказки кнопок
updateButtonTooltips(loginButton, deleteButton, mouseX);
String currentUsername = Minecraft.getInstance().getSession().getUsername();
String screenTitle = "Текущий аккаунт - " + currentUsername + "";
drawCenteredString(matrixStack, font, screenTitle, this.width / 2, 8, COLOR_WHITE);
int listWidth = LIST_WIDTH;
int listX = this.width / 2 - listWidth / 2;
int listY = ACCOUNT_LIST_START_Y;
int visibleAccounts = MAX_LIST_HEIGHT / ACCOUNT_LIST_ENTRY_HEIGHT;
int startIndex = scrollOffset / ACCOUNT_LIST_ENTRY_HEIGHT;
int endIndex = Math.min(filteredAccounts.size(), startIndex + visibleAccounts);
int maxRenderY = this.height - 35 - ACCOUNT_LIST_ENTRY_HEIGHT;
//Отрисовка фона списка аккаунтов
fill(matrixStack, listX, listY, listX + listWidth, listY + MAX_LIST_HEIGHT, 0x80000000);
if (!filteredAccounts.isEmpty()) {
for (int i = startIndex; i < endIndex; i++) {
int entryY = listY + (i - startIndex) * ACCOUNT_LIST_ENTRY_HEIGHT - (scrollOffset % ACCOUNT_LIST_ENTRY_HEIGHT);
if (entryY + ACCOUNT_LIST_ENTRY_HEIGHT <= maxRenderY) {
Account account = filteredAccounts.get(i);
String accountName = account.username;
// Отрисовка выделения для выбранного аккаунта
if (selectedAccount != null && accountName.equals(selectedAccount.username)) {
fill(matrixStack, listX, entryY, listX + listWidth, entryY + ACCOUNT_LIST_ENTRY_HEIGHT, COLOR_SELECTED);
fill(matrixStack, listX + 1, entryY + 1, listX + listWidth - 1, entryY + ACCOUNT_LIST_ENTRY_HEIGHT - 1, COLOR_BACKGROUND);
}
//Отрисовка иконки головы игрока
Minecraft.getInstance().getTextureManager().bindTexture(STEVE_SKIN);
GlStateManager.enableBlend();
drawScaledCustomSizeModalRect(listX + 2, entryY + 2, 8, 8, 8, 8, 16, 16, 64, 64);
GlStateManager.disableBlend();
//Отрисовка имени аккаунта с разным цветом для аккаунта текущей сессии
int textColor = accountName.equals(currentUsername) ? COLOR_GREEN : COLOR_WHITE;
drawString(matrixStack, font, accountName, listX + 21, entryY + 6, textColor);
}
//Отрисовка полосы прокрутки
if (filteredAccounts.size() > visibleAccounts) {
int scrollBarHeight = MAX_LIST_HEIGHT * visibleAccounts / filteredAccounts.size();
int scrollBarY = listY + (scrollOffset * (MAX_LIST_HEIGHT - scrollBarHeight)) / (filteredAccounts.size() * ACCOUNT_LIST_ENTRY_HEIGHT - MAX_LIST_HEIGHT);
fill(matrixStack, listX + listWidth + 2, scrollBarY, listX + listWidth + 6, scrollBarY + scrollBarHeight, COLOR_WHITE);
}
// Отрисовка выделения, если мышь находится над определенным аккаунтом
if (mouseX >= listX && mouseX < listX + listWidth && mouseY >= entryY && mouseY < entryY + ACCOUNT_LIST_ENTRY_HEIGHT) {
fill(matrixStack, listX + 2, entryY + 2, listX + 18, entryY + 18, COLOR_HIGHLIGHT);
}
}
} else {
String noAccountsText = "Аккаунт не найден!";
drawCenteredString(matrixStack, font, noAccountsText, this.width / 2, listY + 20, COLOR_WHITE);
}
//Отрисовка searchField и текста-заполнителя
searchField.render(matrixStack, mouseX, mouseY, partialTicks);
renderSearchFieldPlaceholder(matrixStack);
//Отрисовка textField виджета
if (fieldWidget.getVisible()) {
fieldWidget.render(matrixStack, mouseX, mouseY, partialTicks);
}
}
private void renderSearchFieldPlaceholder(MatrixStack matrixStack) {
String searchFieldText = "Поиск аккаунта";
int searchFieldX = this.width / 2 - this.font.getStringWidth(searchFieldText) / 2 - 60;
int searchFieldY = 26;
if (!searchField.isFocused() && searchField.getText().isEmpty()) {
this.font.drawString(matrixStack, searchFieldText, searchFieldX, searchFieldY, COLOR_HINT);
}
}
private void updateButtonTooltips(Button loginButton, Button deleteButton, int mouseX) {
if (loginButton != null) {
loginButton.setMessage(new StringTextComponent(selectedAccount != null ? "Войти" : "Войти"));
}
if (deleteButton != null) {
deleteButton.setMessage(new StringTextComponent(selectedAccount != null ? "Удалить" : "Удалить"));
}
if (mouseOverButton(loginButton, mouseX)) {
renderTooltip(new StringTextComponent("Войти в выбранный аккаунт"), mouseX, mouseY);
}
if (mouseOverButton(deleteButton, mouseX)) {
renderTooltip(new StringTextComponent("Удалить выбранный аккаунт"), mouseX, mouseY);
}
}
private void renderTooltip(StringTextComponent войтиВВыбранныйАккаунт, int mouseX, int mouseY) {
}
// Вспомогательный метод для проверки, находится ли мышь над кнопкой
private boolean mouseOverButton(Button button, int mouseX) {
return button != null && mouseX >= button.x && mouseX < button.x + button.getWidth() &&
mouseY >= button.y && mouseY < button.y + button.getHeightRealms();
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
int listWidth = LIST_WIDTH;
int listX = this.width / 2 - listWidth / 2;
int visibleAccounts = MAX_LIST_HEIGHT / ACCOUNT_LIST_ENTRY_HEIGHT;
int startIndex = scrollOffset / ACCOUNT_LIST_ENTRY_HEIGHT;
int endIndex = Math.min(filteredAccounts.size(), startIndex + visibleAccounts);
int maxRenderY = this.height - 35 - ACCOUNT_LIST_ENTRY_HEIGHT;
if (button == 0) { //Левая кнопка мыши
long currentTime = System.currentTimeMillis();
for (int i = startIndex; i < endIndex; i++) {
int entryY = ACCOUNT_LIST_START_Y + (i - startIndex) * ACCOUNT_LIST_ENTRY_HEIGHT - (scrollOffset % ACCOUNT_LIST_ENTRY_HEIGHT);
if (entryY + ACCOUNT_LIST_ENTRY_HEIGHT <= maxRenderY && mouseX >= listX && mouseX < listX + listWidth && mouseY >= entryY && mouseY < entryY + ACCOUNT_LIST_ENTRY_HEIGHT) {
selectedAccount = filteredAccounts.get(i);
if (currentTime - lastClickTime < DOUBLE_CLICK_INTERVAL) {
loginSelectedAccount();
}
lastClickTime = currentTime;
return true;
}
}
}
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
if (this.isScrolling) {
this.isScrolling = false;
return true;
}
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
if (this.isScrolling) {
int deltaYInt = (int) mouseY - this.lastMouseY;
this.scrollOffset -= deltaYInt;
this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, filteredAccounts.size() * ACCOUNT_LIST_ENTRY_HEIGHT - MAX_LIST_HEIGHT));
this.lastMouseY = (int) mouseY;
return true;
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
}
private void filterAccounts(String query) {
if (query.isEmpty()) {
filteredAccounts = new ArrayList<>(accounts);
} else {
filteredAccounts.clear();
for (Account account : accounts) {
if (account.username.toLowerCase().contains(query.toLowerCase())) {
filteredAccounts.add(account);
}
}
}
}
private void loginSelectedAccount() {
if (selectedAccount != null) {
Minecraft.getInstance().session = new Session(selectedAccount.username, "", "", "mojang");
}
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
if (isMouseOverScreen(mouseX, mouseY)) {
scrollOffset -= (int) delta * ACCOUNT_LIST_ENTRY_HEIGHT;
int maxScroll = Math.max(0, filteredAccounts.size() * ACCOUNT_LIST_ENTRY_HEIGHT - MAX_LIST_HEIGHT);
scrollOffset = Math.max(0, Math.min(scrollOffset, maxScroll));
return true;
}
return super.mouseScrolled(mouseX, mouseY, delta);
}
private boolean isMouseOverScreen(double mouseX, double mouseY) {
int listWidth = LIST_WIDTH;
int listX = this.width / 2 - listWidth / 2;
int listY = ACCOUNT_LIST_START_Y;
return mouseX >= listX && mouseX < listX + listWidth && mouseY >= listY && mouseY < listY + MAX_LIST_HEIGHT;
}
private void deleteSelectedAccount() {
if (selectedAccount != null) {
accounts.remove(selectedAccount);
saveAccounts();
selectedAccount = null;
}
}
// Проверить, является ли имя пользователя допустимым
private boolean isValidUsername(String username) {
if (username == null || username.length() < 3 || username.length() > 16) {
return false;
}
return username.matches("[a-zA-Z0-9-_]+");
}
// Сохранить аккаунты в файл
private void saveAccounts() {
try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Paths.get(ACCOUNTS_FILE_PATH)))) {
Set<String> uniqueUsernames = new HashSet<>();
for (Account account : accounts) {
if (uniqueUsernames.add(account.username.toLowerCase()) && isValidUsername(account.username)) {
out.println(account.username);
}
}
} catch (IOException e) {
LOGGER.error("Не удалось сохранить аккаунты", e);
}
}
// Загрузить аккаунты из файла
private void loadAccounts() {
File file = new File(ACCOUNTS_FILE_PATH);
if (file.exists()) {
try (Scanner scanner = new Scanner(file)) {
accounts.clear();
Set<String> uniqueUsernames = new HashSet<>();
while (scanner.hasNextLine()) {
String username = scanner.nextLine();
if (uniqueUsernames.add(username.toLowerCase()) && isValidUsername(username)) {
accounts.add(new Account(username));
}
}
} catch (IOException e) {
LOGGER.error("Не удалось загрузить аккаунты", e);
}
}
if (searchField != null) {
filterAccounts(searchField.getText());
}
}
@Override
public void onClose() {
saveAccounts();
super.onClose();
}
}
[CODE]package im.expensive.another.screen;
import com.mojang.blaze3d.matrix.MatrixStack;
import io.netty.util.internal.ThreadLocalRandom;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.util.text.StringTextComponent;
import java.util.Random;
public class AddAccountScreen extends Screen {
private TextFieldWidget fieldWidget;
private Button loginButton;
private Button randomButton;
private int textWidth = 200;
private int buttonHeight = 20;
private int randomButtonWidth = 50;
private int loginButtonWidth = 150;
String currentUsername = Minecraft.getInstance().getSession().getUsername();
AccountManagement accountManagement = new AccountManagement();
public AddAccountScreen() {
super(new StringTextComponent("Добавь новый аккаунт"));
}
@Override
public void init(Minecraft minecraft, int width, int height) {
super.init(minecraft, width, height);
int textFieldX = this.width / 2 - textWidth / 2;
int textFieldY = this.height / 2 - 60;
int buttonY = textFieldY + 25;
fieldWidget = new TextFieldWidget(font, textFieldX, textFieldY, textWidth, 20, new StringTextComponent("Аккаунт")) {
@Override
public boolean charTyped(char codePoint, int modifiers) {
if (Character.toString(codePoint).matches("[a-zA-Z0-9-_]") || codePoint == '\b') {
return super.charTyped(codePoint, modifiers);
}
return false;
}
};
fieldWidget.setMaxStringLength(16);
addButton(fieldWidget);
loginButton = new Button(textFieldX, buttonY, loginButtonWidth, buttonHeight, new StringTextComponent("Войти"), button -> {
String username = fieldWidget.getText().trim();
if (!username.isEmpty()) {
accountManagement.addAccount(username);
Minecraft.getInstance().displayGuiScreen(new AccountManagement());
}
});
addButton(loginButton);
randomButton = new Button(textFieldX + loginButtonWidth, buttonY, randomButtonWidth, buttonHeight, new StringTextComponent("Рандом"), button -> {
Random random = new Random();
String randomNick = generateRandomName();
fieldWidget.setText(randomNick);
});
addButton(randomButton);
Button cancelButton = new Button(textFieldX, buttonY + 25, textWidth, buttonHeight, new StringTextComponent("Аккаунты"), button -> {
Minecraft.getInstance().displayGuiScreen(new AccountManagement());
});
addButton(cancelButton);
}
private String generateRandomName() {
String[] prefixes = {"EvgenBro", "Oskar","Maksim", "Gamer", "Clouse", "Vitya", "4len" ,"Sasho","NEFOR", "Sasha", "SANEK", "SANA", "Muhamad", "Muhamed", "MoonBr", "DragonYT", "YTMAX", "Quantum", "Alpha", "Omega", "Ultra", "Mega", "Super", "Hyper"};
String[] suffixes = {"Hi", "Go", "Gd", "YT","zah","Vit","Mak", "AnDrEY", "Mr", "Sash", "4len","Game", "Bro", "Ivan", "TV", "Pro", "God", "Lord", "King", "Boss", "Max"};
String prefix = prefixes[ThreadLocalRandom.current().nextInt(prefixes.length)];
String suffix = suffixes[ThreadLocalRandom.current().nextInt(suffixes.length)];
int number = ThreadLocalRandom.current().nextInt(100, 999);
return prefix + suffix + number;
}
@Override
public void tick() {
super.tick();
fieldWidget.tick();
loginButton.active = fieldWidget.getText().trim().length() >= 3;
}
@Override
public void render(MatrixStack matrixStack, int mouseX, int mouseY, float partialTicks) {
this.renderDirtBackground(0);
super.render(matrixStack, mouseX, mouseY, partialTicks);
fieldWidget.render(matrixStack, mouseX, mouseY, partialTicks);
loginButton.render(matrixStack, mouseX, mouseY, partialTicks);
randomButton.render(matrixStack, mouseX, mouseY, partialTicks);
if (fieldWidget.getText().isEmpty()) {
drawString(matrixStack, font, "Введите никнейм", width / 2 - textWidth / 2, height / 2 - 75, 0xCCCCCC);
}
if (!fieldWidget.getText().isEmpty()) {
drawString(matrixStack, font, "Введите никнейм", width / 2 - textWidth / 2, height / 2 - 75, 0xCCCCCC);
}
if (mouseOverButton(loginButton, mouseX, mouseY)) {
if (!loginButton.active) {
renderTooltip(matrixStack, new StringTextComponent("Введите никнейм от 3 символов"), mouseX, mouseY);
} else {
renderTooltip(matrixStack, new StringTextComponent("Нажми что-бы войти"), mouseX, mouseY);
}
}
if (mouseOverButton(randomButton, mouseX, mouseY)) {
renderTooltip(matrixStack, new StringTextComponent("Рандомный никнейм"), mouseX, mouseY);
}
drawCenteredString(matrixStack, font, "Аккаунты", this.width / 2, 10, 0xFFFFFF);
}
private boolean mouseOverButton(Button button, int mouseX, int mouseY) {
int buttonX1 = button.x;
int buttonY1 = button.y;
int buttonX2 = button.x + button.getWidth();
int buttonY2 = button.y + button.getHeightRealms();
return mouseX >= buttonX1 && mouseY >= buttonY1 && mouseX < buttonX2 && mouseY < buttonY2;
}
}