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
4 changes: 2 additions & 2 deletions RemotelyMod/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
textile = "1.0.0-beta.13"
omnicore = "1.0.0-beta.40"
textile = "1.0.0-beta.8"
omnicore = "1.0.0-beta.38"

[libraries]
textile = { group = "dev.deftu", name = "textile", version.ref = "textile" }
Expand Down
Binary file modified RemotelyMod/libs/ReScreen-1.0.jar
Binary file not shown.
Binary file modified RemotelyMod/libs/Rebase-1.0-SNAPSHOT.jar
Binary file not shown.
Binary file modified RemotelyMod/libs/Remotely-App.jar
Binary file not shown.
Binary file modified libs/ReScreen-1.0.jar
Binary file not shown.
Binary file modified libs/Rebase-1.0-SNAPSHOT.jar
Binary file not shown.
3 changes: 2 additions & 1 deletion src/main/java/redxax/oxy/remotely/RemotelyManager.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package redxax.oxy.remotely;

import redxax.oxy.remotely.config.RemotelyConfigManager;
import restudio.rebase.backend.impl.ReStudioBackend;
import restudio.rebase.update.ApplicationUpdateManager;
import restudio.rebase.IRebaseManager;
import restudio.rebase.account.AccountManager;
Expand All @@ -13,7 +14,6 @@
import restudio.rebase.preset.ResourceListManager;
import restudio.rebase.resource.InstanceResourceManager;
import restudio.rebase.resource.ResourceMetadataManager;
import restudio.rebase.resource.ResourceStateManager;
import restudio.rebase.resource.UpdateManager;
import restudio.rebase.resource.provider.*;

Expand Down Expand Up @@ -111,6 +111,7 @@ private void init() {
javaManager.refreshRuntimes();
BackendFactory.register("LOCAL", (cfg, inst) -> new LocalBackend(cfg != null ? cfg : new BackendConfig("LOCAL", new java.util.HashMap<>()), inst));
BackendFactory.register("SSH", SshBackend::new);
BackendFactory.register("RESTUDIO", ReStudioBackend::new);

if (configManager.isUpdateCheckOnStartup()) {
applicationUpdateManager.checkForUpdates().thenAccept(updateOpt -> updateOpt.ifPresent(releaseInfo -> restudio.rescreen.ui.core.ScreenManager.getInstance().execute(() -> UpdateAvailablePopup.show(releaseInfo, applicationUpdateManager))));
Expand Down
39 changes: 0 additions & 39 deletions src/main/java/redxax/oxy/remotely/data/managed/ManagedPlayer.java

This file was deleted.

This file was deleted.

This file was deleted.

30 changes: 30 additions & 0 deletions src/main/java/redxax/oxy/remotely/data/player/PlayerRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package redxax.oxy.remotely.data.player;

import redxax.oxy.remotely.data.player.model.UnifiedPlayer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class PlayerRegistry {
private final Map<UUID, UnifiedPlayer> players = new ConcurrentHashMap<>();

public UnifiedPlayer getOrCreate(UUID uuid, String name) {
return players.computeIfAbsent(uuid, k -> new UnifiedPlayer(k, name));
}

public UnifiedPlayer get(UUID uuid) {
return players.get(uuid);
}

public List<UnifiedPlayer> getAll() {
return new ArrayList<>(players.values());
}

public void cleanup() {

}
}
111 changes: 111 additions & 0 deletions src/main/java/redxax/oxy/remotely/data/player/PlayerService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package redxax.oxy.remotely.data.player;

import redxax.oxy.remotely.data.player.action.IActionExecutor;
import redxax.oxy.remotely.data.player.model.UnifiedPlayer;
import redxax.oxy.remotely.data.player.source.IPlayerSource;
import restudio.rescreen.debug.DebugManager;

import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;

public class PlayerService {
private final PlayerRegistry registry = new PlayerRegistry();
private final List<IPlayerSource> sources = new CopyOnWriteArrayList<>();
private final List<IActionExecutor> executors = new CopyOnWriteArrayList<>();
private final List<Consumer<List<UnifiedPlayer>>> listeners = new CopyOnWriteArrayList<>();

public void registerSource(IPlayerSource source) {
sources.add(source);
source.init(this);
source.enable();
}

public void registerExecutor(IActionExecutor executor) {
executors.add(executor);
}

public void refreshSources() {
for (IPlayerSource source : sources) {
source.refresh();
}
}

public void submitUpdate(PlayerUpdateBatch batch) {
for (PlayerUpdateBatch.PlayerUpdate update : batch.getUpdates()) {
UnifiedPlayer player = registry.getOrCreate(update.getUuid(), update.getName());

if (update.getName() != null && !update.getName().isEmpty()) {
player.setName(update.getName());
}

String src = batch.getSource();
int prio = batch.getPriority();

if (update.getOnline() != null) player.updateOnline(update.getOnline(), src, prio);
if (update.getPing() != null) player.updatePing(update.getPing(), src, prio);
if (update.getOp() != null) player.updateOp(update.getOp(), src, prio);
if (update.getLastSeen() != null) player.updateLastSeen(update.getLastSeen(), src, prio);

if (update.shouldClearBan()) player.updateBan(null, src, prio);
else if (update.getBan() != null) player.updateBan(update.getBan(), src, prio);

if (update.shouldClearIp()) player.updateIp(null, src, prio);
else if (update.getIp() != null) player.updateIp(update.getIp(), src, prio);
}

notifyListeners();
}

public CompletableFuture<Void> executeAction(UnifiedPlayer player, String actionType, Object... args) {
DebugManager.getInstance().log("PlayerService", "Executing action: " + actionType + " for " + player.getName());
List<IActionExecutor> candidates = executors.stream()
.filter(e -> {
boolean can = e.canExecute(actionType);
DebugManager.getInstance().log("PlayerService", "Executor " + e.getClass().getSimpleName() + " canExecute(" + actionType + "): " + can);
return can;
})
.sorted(Comparator.comparingInt(IActionExecutor::getPriority).reversed())
.toList();

if (candidates.isEmpty()) {
DebugManager.getInstance().log("PlayerService", "No executors found for " + actionType);
return CompletableFuture.failedFuture(new IllegalStateException("No executor found for action: " + actionType));
}

DebugManager.getInstance().log("PlayerService", "Candidates: " + candidates.size() + ". Starting chain.");
return executeChain(candidates, 0, player, actionType, args);
}

private CompletableFuture<Void> executeChain(List<IActionExecutor> executors, int index, UnifiedPlayer player, String actionType, Object... args) {
if (index >= executors.size()) {
DebugManager.getInstance().log("PlayerService", "Chain exhausted. All failed.");
return CompletableFuture.failedFuture(new IllegalStateException("All executors failed for action: " + actionType));
}

IActionExecutor current = executors.get(index);
DebugManager.getInstance().log("PlayerService", "Trying executor: " + current.getClass().getSimpleName());
return current.execute(player, actionType, args)
.exceptionallyCompose(ex -> {
DebugManager.getInstance().log("PlayerService", "Executor " + current.getClass().getSimpleName() + " failed: " + ex.getMessage());
return executeChain(executors, index + 1, player, actionType, args);
});
}

public void addListener(Consumer<List<UnifiedPlayer>> listener) {
listeners.add(listener);
}

private void notifyListeners() {
List<UnifiedPlayer> allPlayers = registry.getAll();
for (Consumer<List<UnifiedPlayer>> listener : listeners) {
listener.accept(allPlayers);
}
}

public PlayerRegistry getRegistry() {
return registry;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package redxax.oxy.remotely.data.player;

import redxax.oxy.remotely.data.player.model.BanInfo;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class PlayerUpdateBatch {
private final String source;
private final int priority;
private final List<PlayerUpdate> updates = new ArrayList<>();

public PlayerUpdateBatch(String source, int priority) {
this.source = source;
this.priority = priority;
}

public void add(PlayerUpdate update) {
updates.add(update);
}

public String getSource() {
return source;
}

public int getPriority() {
return priority;
}

public List<PlayerUpdate> getUpdates() {
return updates;
}

public static class PlayerUpdate {
private final UUID uuid;
private final String name;

private Boolean online;
private Integer ping;
private Boolean op;
private BanInfo ban;
private String ip;
private Long lastSeen;

public PlayerUpdate(UUID uuid, String name) {
this.uuid = uuid;
this.name = name;
}

public UUID getUuid() { return uuid; }
public String getName() { return name; }

public Boolean getOnline() { return online; }
public void setOnline(Boolean online) { this.online = online; }

public Integer getPing() { return ping; }
public void setPing(Integer ping) { this.ping = ping; }

public Boolean getOp() { return op; }
public void setOp(Boolean op) { this.op = op; }

public BanInfo getBan() { return ban; }
public void setBan(BanInfo ban) { this.ban = ban; }
public boolean shouldClearBan() { return clearBan; }
public void clearBan() { this.clearBan = true; }

public String getIp() { return ip; }
public void setIp(String ip) { this.ip = ip; }
public boolean shouldClearIp() { return clearIp; }
public void clearIp() { this.clearIp = true; }

public Long getLastSeen() { return lastSeen; }
public void setLastSeen(Long lastSeen) { this.lastSeen = lastSeen; }

private boolean clearBan = false;
private boolean clearIp = false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package redxax.oxy.remotely.data.player.action;

import redxax.oxy.remotely.data.player.model.UnifiedPlayer;
import restudio.rebase.backend.feature.PlayerManagementFeature;

import java.util.concurrent.CompletableFuture;

public class BackendActionExecutor implements IActionExecutor {
private final PlayerManagementFeature feature;

public BackendActionExecutor(PlayerManagementFeature feature) {
this.feature = feature;
}

@Override
public boolean canExecute(String actionType) {
return switch (actionType) {
case "kick", "ban", "unban", "op", "deop" -> true;
default -> false;
};
}

@Override
public CompletableFuture<Void> execute(UnifiedPlayer player, String actionType, Object... args) {
return switch (actionType) {
case "kick" -> {
String reason = args.length > 0 ? (String) args[0] : "Kicked by operator";
yield feature.kick(player.getUuid(), reason);
}
case "ban" -> {
String reason = args.length > 0 ? (String) args[0] : "Banned by operator";
boolean ipBan = args.length > 1 && (boolean) args[1];
yield feature.ban(player.getUuid(), reason, ipBan);
}
case "unban" -> feature.unban(player.getUuid());
case "op" -> feature.setOp(player.getUuid(), true);
case "deop" -> feature.setOp(player.getUuid(), false);
default -> CompletableFuture.failedFuture(new UnsupportedOperationException("Unknown action: " + actionType));
};
}

@Override
public int getPriority() {
return 20;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package redxax.oxy.remotely.data.player.action;

import redxax.oxy.remotely.data.player.model.UnifiedPlayer;
import java.util.concurrent.CompletableFuture;

public interface IActionExecutor {
boolean canExecute(String actionType);
CompletableFuture<Void> execute(UnifiedPlayer player, String actionType, Object... args);
int getPriority();
}
Loading
Loading