Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions src/client/java/works/alya/AlyaClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package works.alya;

import works.alya.backend.BackendManager;
import works.alya.command.CommandBuilder;
import works.alya.command.CommandRepository;
import works.alya.command.impl.*;
Expand All @@ -27,8 +28,10 @@
import works.alya.module.impl.combat.AimAssistModule;
import works.alya.module.impl.combat.AttackDelayModule;
import works.alya.module.impl.combat.AutoClickerModule;
import works.alya.module.impl.combat.BacktrackModule;
import works.alya.module.impl.combat.killaura.KillauraModule;
import works.alya.module.impl.movement.*;
import works.alya.module.impl.movement.step.StepModule;
import works.alya.module.impl.utility.antivoid.AntiVoidModule;
import works.alya.module.impl.utility.disabler.DisablerModule;
import works.alya.module.impl.visual.*;
Expand Down Expand Up @@ -67,14 +70,15 @@ public class AlyaClient implements ClientModInitializer {
public static final String MOD_ID = "alya";
public static final Logger LOGGER = LogManager.getLogger(MOD_ID);
public static AlyaClient INSTANCE = new AlyaClient();
private static String tenaState = "loading";
private static String alyaState = "loading";
private final ModuleRepository moduleRepository = ModuleRepository.getInstance();
private static Vec3d lastPos = null;
private static long lastMoveTime = 0;
private static double lastBps = 0.0;
private static final Set<Integer> previouslyPressedKeys = new HashSet<>();
private static EventBus eventBus;
private static boolean wasInGame = false;
public static BackendManager backendManager;

@Override
public void onInitializeClient() {
Expand Down Expand Up @@ -124,16 +128,20 @@ public void onInitializeClient() {

if(isInGame && !wasInGame) {
VisualManager.getInstance().applyVisualData();
LOGGER.info("Applied visual module data after joining world");
}

if(!isInGame && wasInGame) {
VisualManager.getInstance().saveVisualData();
LOGGER.info("Saved visual module data after leaving world");
}

wasInGame = isInGame;
});

String userName = System.getProperty("user.name");
String userIdHashed = String.valueOf(userName.hashCode());
backendManager = new BackendManager(userIdHashed);

backendManager.goOnline();
}

private static void initializeModules() {
Expand Down Expand Up @@ -174,7 +182,9 @@ private static void initializeModules() {
new TargetStrafeModule(),
new SpeedMonitorModule(),
new AutoClickerModule(),
new AimAssistModule()
new AimAssistModule(),
new BacktrackModule(),
new StepModule()
);
}

Expand All @@ -200,11 +210,11 @@ public ModuleRepository getModuleRepository() {
}

public static String getState() {
return tenaState;
return alyaState;
}

public static void setState(String state) {
tenaState = state;
alyaState = state;
}

public static String getName() {
Expand Down Expand Up @@ -265,4 +275,8 @@ public static String getBps() {
public static String getTime() {
return new java.text.SimpleDateFormat("hh:mm a").format(new Date());
}

public static int getUsersOnline() {
return backendManager.getCurrentOnlineCount();
}
}
120 changes: 120 additions & 0 deletions src/client/java/works/alya/backend/BackendAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright (c) Alya Client 2024-2025.
*
* This file belongs to Alya Client,
* an open-source Fabric injection client.
* Rye GitHub: https://github.com/AlyaClient/alya-beta.git
*
* THIS PROJECT DOES NOT HAVE A WARRANTY.
*
* Alya (and subsequently, its files) are all licensed under the MIT License.
* Alya should have come with a copy of the MIT License.
* If it did not, you may obtain a copy here:
* MIT License: https://opensource.org/license/mit
*
*/

package works.alya.backend;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import works.alya.AlyaClient;

public class BackendAPI {
private static final String BASE_URL = "https://rye.thoq.dev/api/v1/users";
private final HttpClient httpClient;
private final ObjectMapper objectMapper;

public BackendAPI() {
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
this.objectMapper = new ObjectMapper();
}

public void joinOnline(String userId, String sessionId) throws Exception {
String requestBody = """
{
"userId": "%s",
"sessionId": "%s",
"action": "join"
}
""".formatted(userId, sessionId);

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.timeout(Duration.ofSeconds(30))
.build();

HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());

if(response.statusCode() != 200) {
AlyaClient.LOGGER.error("Error: {} - {}", response.statusCode(), response.body());
}
}

public void sendHeartbeat(String userId, String sessionId) throws Exception {
String requestBody = """
{
"userId": "%s",
"sessionId": "%s",
"action": "heartbeat"
}
""".formatted(userId, sessionId);

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.timeout(Duration.ofSeconds(30))
.build();

httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}

public void leaveOnline(String userId, String sessionId) throws Exception {
String requestBody = """
{
"userId": "%s",
"sessionId": "%s",
"action": "leave"
}
""".formatted(userId, sessionId);

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.timeout(Duration.ofSeconds(30))
.build();

httpClient.send(request, HttpResponse.BodyHandlers.ofString());
}

public int getOnlineCount() throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL))
.header("Content-Type", "application/json")
.GET()
.timeout(Duration.ofSeconds(30))
.build();

HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());

if(response.statusCode() == 200) {
JsonNode jsonResponse = objectMapper.readTree(response.body());
return jsonResponse.get("onlineUsers").asInt();
}
return -1;
}
}
77 changes: 77 additions & 0 deletions src/client/java/works/alya/backend/BackendManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) Alya Client 2024-2025.
*
* This file belongs to Alya Client,
* an open-source Fabric injection client.
* Rye GitHub: https://github.com/AlyaClient/alya-beta.git
*
* THIS PROJECT DOES NOT HAVE A WARRANTY.
*
* Alya (and subsequently, its files) are all licensed under the MIT License.
* Alya should have come with a copy of the MIT License.
* If it did not, you may obtain a copy here:
* MIT License: https://opensource.org/license/mit
*
*/

package works.alya.backend;

import works.alya.AlyaClient;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.UUID;

public class BackendManager {
private final BackendAPI client;
private final ScheduledExecutorService scheduler;
private final String userId;
private final String sessionId;
private volatile boolean isOnline = false;

public BackendManager(String userId) {
this.client = new BackendAPI();
this.scheduler = Executors.newScheduledThreadPool(1);
this.userId = userId;
this.sessionId = UUID.randomUUID().toString();
}

public void goOnline() {
try {
client.joinOnline(userId, sessionId);
isOnline = true;

scheduler.scheduleAtFixedRate(() -> {
if (isOnline) {
try {
client.sendHeartbeat(userId, sessionId);
} catch (Exception e) {
AlyaClient.LOGGER.error("Heartbeat failed: {}", e.getMessage());
}
}
}, 120, 120, TimeUnit.SECONDS);
} catch (Exception e) {
AlyaClient.LOGGER.error("Failed to go online: {}", e.getMessage());
}
}

public void goOffline() {
try {
isOnline = false;
client.leaveOnline(userId, sessionId);
scheduler.shutdown();
} catch (Exception e) {
AlyaClient.LOGGER.error("Failed to go offline: {}", e.getMessage());
}
}

public int getCurrentOnlineCount() {
try {
return client.getOnlineCount();
} catch (Exception e) {
AlyaClient.LOGGER.error("Failed to get online count: {}", e.getMessage());
return -1;
}
}
}
28 changes: 5 additions & 23 deletions src/client/java/works/alya/config/ConfigManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,25 @@ public class ConfigManager {
if(!folder.exists()) {
boolean result = folder.mkdirs();
if(!result) {
LOGGER.info("Bwoah! Failed to create config folder!");
LOGGER.error("Bwoah! Failed to create config folder!");
}
}
}

/**
* Saves the current configuration state of all modules, including their settings
* and keybinds, to a JSON file specified by the provided name.
* Saves the current configuration state of all modules, including their settings,
* to a JSON file specified by the provided name.
* The configuration data includes whether the module is enabled, all associated
* module settings with their respective values and types, and any defined keybinds.
* module settings with their respective values and types.
* If the file does not exist, it is created, and if it does exist, it is overwritten.
*
* @param name The name of the configuration file to save (excluding the file extension).
*/
public static void saveConfig(String name) {
Map<String, Object> config = new LinkedHashMap<>();
Map<String, Object> modulesConfig = new LinkedHashMap<>();
Map<String, Integer> keybinds = new LinkedHashMap<>();

for(Module module : AlyaClient.INSTANCE.getModuleRepository().getModules()) {
// Skip visual modules (except for enabled state) as they are handled by VisualManager
if (module.getCategory() == ModuleCategory.VISUAL) {
Map<String, Object> moduleConfig = new LinkedHashMap<>();
moduleConfig.put("enabled", module.isEnabled());
Expand Down Expand Up @@ -110,13 +108,9 @@ public static void saveConfig(String name) {
modulesConfig.put(module.getName(), moduleConfig);

Integer key = KeybindManager.getInstance().getKeyForModule(module);
if(key != null) {
keybinds.put(module.getName(), key);
}
}

config.put("modules", modulesConfig);
config.put("keybinds", keybinds);

File configFile = new File(CONFIG_FOLDER + name + ".json");
try(Writer writer = new FileWriter(configFile)) {
Expand All @@ -129,7 +123,7 @@ public static void saveConfig(String name) {

/**
* Loads a configuration file by its name, applies the settings to the respective
* modules and keybinds, and updates the client state. If the configuration file does
* modules, and updates the client state. If the configuration file does
* not exist, an error message is displayed in chat.
*
* @param name The name of the configuration file to load, excluding the file extension.
Expand Down Expand Up @@ -181,18 +175,6 @@ public static void loadConfig(String name) {
}
}
}

@SuppressWarnings("unchecked")
Map<String, Double> keybinds = (Map<String, Double>) config.get("keybinds");
if(keybinds != null) {
KeybindManager keybindManager = KeybindManager.getInstance();
for(Map.Entry<String, Double> entry : keybinds.entrySet()) {
Module module = AlyaClient.INSTANCE.getModuleRepository().getModuleByName(entry.getKey());
if(module != null) {
keybindManager.bind(module, entry.getValue().intValue());
}
}
}
}
} catch(IOException ex) {
ex.printStackTrace();
Expand Down
2 changes: 1 addition & 1 deletion src/client/java/works/alya/config/KeybindManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ private void ensureKeybindsFolder() {
if(!folder.exists()) {
boolean result = folder.mkdirs();
if(!result) {
LOGGER.info("Failed to create keybindings folder!");
LOGGER.error("Failed to create keybindings folder!");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,9 @@ private void onPostTick(CallbackInfo ci) {

AlyaClient.getEventBus().dispatch(event);
}

@Inject(method = "close", at = @At("HEAD"))
public void close(CallbackInfo ci) {
AlyaClient.backendManager.goOffline();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ private void renderRyeLogo(LogoDrawer logoDrawer, DrawContext context, int width

@Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTextWithShadow(Lnet/minecraft/client/font/TextRenderer;Ljava/lang/String;III)V"))
private void replaceVersionText(DrawContext context, net.minecraft.client.font.TextRenderer textRenderer, String text, int x, int y, int color) {
// we ignore demo, modded and version
String usersOnline = String.format("%s online", AlyaClient.getUsersOnline());

TextRendererUtility.renderText(context, usersOnline, ColorUtility.Colors.WHITE, x, y, false);
}

@Inject(method = "init", at = @At("HEAD"))
Expand Down
Loading