diff --git a/docs/abstract_entity_versions.md b/docs/abstract_entity_versions.md new file mode 100644 index 00000000..c2231c1c --- /dev/null +++ b/docs/abstract_entity_versions.md @@ -0,0 +1,7 @@ +abstract entity version log + +# 0 / null +no version / outdated + +# 1 - server module migration update +this version introduces module settings via separate entities \ No newline at end of file diff --git a/src/agent/main/java/org/comroid/mcsd/agent/AgentRunner.java b/src/agent/main/java/org/comroid/mcsd/agent/AgentRunner.java index 944d2551..2ccbda9b 100644 --- a/src/agent/main/java/org/comroid/mcsd/agent/AgentRunner.java +++ b/src/agent/main/java/org/comroid/mcsd/agent/AgentRunner.java @@ -7,15 +7,18 @@ import org.comroid.api.DelegateStream; import org.comroid.api.Polyfill; import org.comroid.mcsd.agent.controller.ConsoleController; +import org.comroid.mcsd.core.MCSD; import org.comroid.mcsd.core.ServerManager; import org.comroid.mcsd.core.entity.*; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.system.Agent; import org.comroid.mcsd.core.module.console.ConsoleModule; import org.comroid.mcsd.core.module.status.BackupModule; import org.comroid.mcsd.core.module.local.LocalExecutionModule; import org.comroid.mcsd.core.module.status.UpdateModule; import org.comroid.mcsd.core.module.status.StatusModule; -import org.comroid.mcsd.core.repo.ServerRepo; -import org.comroid.mcsd.core.repo.ShRepo; +import org.comroid.mcsd.core.repo.server.ServerRepo; +import org.comroid.mcsd.core.util.ApplicationContextProvider; import org.comroid.mcsd.util.Utils; import org.comroid.util.Streams; import org.jetbrains.annotations.NotNull; @@ -25,8 +28,6 @@ import org.springframework.stereotype.Service; import java.io.PrintStream; -import java.time.Duration; -import java.time.Instant; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -202,7 +203,7 @@ public String detach(String[] args, ConsoleController.Connection con) { attached = null; return "Detached"; } - +/* @Command(usage = " [-na]") public Object create(String[] args, ConsoleController.Connection con) { if (!Utils.SuperAdmins.contains(con.getUser().getId())) @@ -250,7 +251,7 @@ public Object create(String[] args, ConsoleController.Connection con) { server.setOwner(con.getUser()).setName(name); return serverRepo.save(server) + " created"; } - + */ @Command(usage = "") public String shutdown(ConsoleController.Connection con) { if (!Utils.SuperAdmins.contains(con.getUser().getId())) @@ -278,12 +279,13 @@ public void handleResponse(Command.Delegate cmd, @NotNull Object response, Objec out.println(response); } + @Deprecated public Stream streamServers() { return Streams.of(bean(ServerRepo.class).findAllForAgent(getMe().getId())); } public Stream streamServerStatusMsgs() { - return streamServers().map(Server::toString); + return bean(MCSD.class).servers().stream().map(Server::toString); } public void execute(String cmd, ConsoleController.Connection con) { diff --git a/src/agent/main/java/org/comroid/mcsd/agent/Program.java b/src/agent/main/java/org/comroid/mcsd/agent/Program.java index fe71e976..498c9895 100644 --- a/src/agent/main/java/org/comroid/mcsd/agent/Program.java +++ b/src/agent/main/java/org/comroid/mcsd/agent/Program.java @@ -12,29 +12,18 @@ import org.comroid.mcsd.api.dto.McsdConfig; import org.comroid.mcsd.core.MCSD; import org.comroid.mcsd.core.ServerManager; -import org.comroid.mcsd.core.entity.Agent; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.mcsd.core.entity.system.Agent; +import org.comroid.mcsd.core.entity.server.Server; import org.comroid.mcsd.core.exception.EntityNotFoundException; -import org.comroid.mcsd.core.module.*; -import org.comroid.mcsd.core.module.discord.DiscordModule; -import org.comroid.mcsd.core.module.player.ConsolePlayerEventModule; -import org.comroid.mcsd.core.module.local.LocalExecutionModule; -import org.comroid.mcsd.core.module.local.LocalFileModule; -import org.comroid.mcsd.core.module.player.PlayerListModule; -import org.comroid.mcsd.core.module.status.StatusModule; -import org.comroid.mcsd.core.module.status.UptimeModule; -import org.comroid.mcsd.core.repo.AgentRepo; -import org.comroid.mcsd.core.repo.ServerRepo; +import org.comroid.mcsd.core.repo.system.AgentRepo; +import org.comroid.mcsd.core.repo.server.ServerRepo; import org.comroid.util.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.ImportResource; -import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.*; import org.springframework.http.HttpStatus; import java.util.List; @@ -66,24 +55,10 @@ public Agent me(@Autowired AgentInfo agentInfo, @Autowired AgentRepo agents) { .orElseThrow(() -> new EntityNotFoundException(Agent.class, agentInfo.getAgent())); } - @Bean - public List> serverModuleFactories() { - return List.of( - StatusModule.Factory, - LocalFileModule.Factory, - UptimeModule.Factory, - //todo: fix UpdateModule.Factory, - LocalExecutionModule.Factory, - //todo: fix BackupModule.Factory, - ConsolePlayerEventModule.Factory, - PlayerListModule.Factory, - DiscordModule.Factory - ); - } - @Bean @Unique @Lazy(false) + @DependsOn("migrateEntities") public List servers(@Autowired ServerRepo serverRepo, @Autowired Agent me) { return Streams.of(serverRepo.findAllForAgent(me.getId())).toList(); } diff --git a/src/agent/main/java/org/comroid/mcsd/agent/controller/ApiController.java b/src/agent/main/java/org/comroid/mcsd/agent/controller/ApiController.java index 6eef874a..b51169d9 100644 --- a/src/agent/main/java/org/comroid/mcsd/agent/controller/ApiController.java +++ b/src/agent/main/java/org/comroid/mcsd/agent/controller/ApiController.java @@ -2,26 +2,24 @@ import jakarta.servlet.http.HttpSession; import lombok.extern.slf4j.Slf4j; -import org.comroid.api.IntegerAttribute; -import org.comroid.api.Named; import org.comroid.mcsd.agent.AgentRunner; +import org.comroid.mcsd.core.MCSD; import org.comroid.mcsd.core.entity.AbstractEntity; -import org.comroid.mcsd.core.entity.Agent; -import org.comroid.mcsd.core.entity.Server; -import org.comroid.mcsd.core.entity.User; +import org.comroid.mcsd.core.entity.system.Agent; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.system.User; import org.comroid.mcsd.core.exception.InsufficientPermissionsException; -import org.comroid.mcsd.core.repo.UserRepo; -import org.jetbrains.annotations.NotNull; +import org.comroid.mcsd.core.repo.system.UserRepo; +import org.comroid.mcsd.core.util.ApplicationContextProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; -import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; + +import static org.comroid.mcsd.core.util.ApplicationContextProvider.bean; @Slf4j @Controller @@ -43,7 +41,7 @@ public User user(HttpSession session) { @ResponseBody @GetMapping("/webapp/servers") public List servers(HttpSession session) { - return runner.streamServers() + return bean(MCSD.class).servers().stream() .filter(x->x.hasPermission(user(session), AbstractEntity.Permission.Any)) .toList(); } diff --git a/src/agent/main/java/org/comroid/mcsd/agent/controller/ConsoleController.java b/src/agent/main/java/org/comroid/mcsd/agent/controller/ConsoleController.java index 922b2e69..570fbb0b 100644 --- a/src/agent/main/java/org/comroid/mcsd/agent/controller/ConsoleController.java +++ b/src/agent/main/java/org/comroid/mcsd/agent/controller/ConsoleController.java @@ -8,10 +8,10 @@ import org.comroid.api.Event; import org.comroid.mcsd.agent.AgentRunner; import org.comroid.mcsd.agent.config.WebSocketConfig; -import org.comroid.mcsd.core.entity.Server; -import org.comroid.mcsd.core.entity.User; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.system.User; import org.comroid.mcsd.core.module.local.LocalExecutionModule; -import org.comroid.mcsd.core.repo.UserRepo; +import org.comroid.mcsd.core.repo.system.UserRepo; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/core/main/java/org/comroid/mcsd/core/MCSD.java b/src/core/main/java/org/comroid/mcsd/core/MCSD.java index ebf84a36..daa38552 100644 --- a/src/core/main/java/org/comroid/mcsd/core/MCSD.java +++ b/src/core/main/java/org/comroid/mcsd/core/MCSD.java @@ -2,27 +2,61 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.mysql.cj.jdbc.Driver; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.sshd.client.ClientBuilder; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier; import org.comroid.api.DelegateStream; +import org.comroid.api.Polyfill; import org.comroid.api.io.FileHandle; import org.comroid.api.os.OS; import org.comroid.mcsd.api.dto.McsdConfig; import org.comroid.mcsd.core.entity.*; +import org.comroid.mcsd.core.entity.module.ModulePrototype; +import org.comroid.mcsd.core.entity.module.console.McsdCommandModulePrototype; +import org.comroid.mcsd.core.entity.module.discord.DiscordModulePrototype; +import org.comroid.mcsd.core.entity.module.local.LocalExecutionModulePrototype; +import org.comroid.mcsd.core.entity.module.local.LocalFileModulePrototype; +import org.comroid.mcsd.core.entity.module.local.LocalShellModulePrototype; +import org.comroid.mcsd.core.entity.module.player.ConsolePlayerEventModulePrototype; +import org.comroid.mcsd.core.entity.module.player.PlayerListModulePrototype; +import org.comroid.mcsd.core.entity.module.ssh.SshFileModulePrototype; +import org.comroid.mcsd.core.entity.module.status.BackupModulePrototype; +import org.comroid.mcsd.core.entity.module.status.StatusModulePrototype; +import org.comroid.mcsd.core.entity.module.status.UpdateModulePrototype; +import org.comroid.mcsd.core.entity.module.status.UptimeModulePrototype; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.system.Agent; +import org.comroid.mcsd.core.entity.system.DiscordBot; +import org.comroid.mcsd.core.entity.system.ShConnection; +import org.comroid.mcsd.core.entity.system.User; import org.comroid.mcsd.core.exception.EntityNotFoundException; import org.comroid.mcsd.core.exception.BadRequestException; import org.comroid.mcsd.core.module.discord.DiscordAdapter; -import org.comroid.mcsd.core.repo.*; +import org.comroid.mcsd.core.repo.module.ModuleRepo; +import org.comroid.mcsd.core.repo.server.ServerRepo; +import org.comroid.mcsd.core.repo.system.AgentRepo; +import org.comroid.mcsd.core.repo.system.DiscordBotRepo; +import org.comroid.mcsd.core.repo.system.ShRepo; +import org.comroid.mcsd.core.repo.system.UserRepo; +import org.comroid.mcsd.core.util.ApplicationContextProvider; import org.comroid.util.Debug; import org.comroid.util.REST; +import org.comroid.util.Streams; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.SingletonBeanRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.*; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -30,24 +64,45 @@ import javax.sql.DataSource; import java.io.IOException; -import java.util.UUID; +import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static org.comroid.mcsd.core.util.ApplicationContextProvider.bean; @Slf4j +@Getter @Configuration @ImportResource({"classpath:baseBeans.xml"}) @ComponentScan(basePackages = "org.comroid.mcsd.core") @EntityScan(basePackages = "org.comroid.mcsd.core.entity") @EnableJpaRepositories(basePackages = "org.comroid.mcsd.core.repo") public class MCSD { + @Lazy @Autowired private DefaultListableBeanFactory beanRegistry; + @Lazy @Autowired private EntityManager entityManager;; @Lazy @Autowired private UserRepo users; @Lazy @Autowired private ServerRepo servers; @Lazy @Autowired private AgentRepo agents; @Lazy @Autowired private ShRepo shRepo; @Lazy @Autowired private DiscordBotRepo discordBotRepo; + @Lazy @Autowired private ModuleRepo modules_mcsd; + @Lazy @Autowired private ModuleRepo modules_discord; + @Lazy @Autowired private ModuleRepo modules_localExecution; + @Lazy @Autowired private ModuleRepo modules_localFiles; + @Lazy @Autowired private ModuleRepo modules_localShell; + @Lazy @Autowired private ModuleRepo modules_consolePlayerEvents; + @Lazy @Autowired private ModuleRepo modules_playerList; + @Lazy @Autowired private ModuleRepo modules_sshFile; + @Lazy @Autowired private ModuleRepo modules_backup; + @Lazy @Autowired private ModuleRepo modules_status; + @Lazy @Autowired private ModuleRepo modules_update; + @Lazy @Autowired private ModuleRepo modules_uptime; + @Bean(name = "configDir") @Order(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnExpression(value = "environment.containsProperty('DEBUG')") @@ -126,6 +181,102 @@ public ScheduledFuture shutdownForAutoUpdateTask(@Autowired ScheduledExecutor }, 72, 72, TimeUnit.HOURS); } + @Bean + @Lazy(false) + @Transactional + @SuppressWarnings("deprecation") + @Order(Ordered.HIGHEST_PRECEDENCE) + @DependsOn("applicationContextProvider") + public Set migrateEntities() { + class Helper { + void getOrMigrate(Server server, ModulePrototype.Type type, Supplier migratedObj) { + var repo = type.getRepo().apply(MCSD.this); + if (repo.findByServerIdAndDtype(server.getId(), type.name()).isPresent()) + return; + var migrate = migratedObj.get(); + save(repo, migrate); + repo.save(Polyfill.uncheckedCast(migrate)); + } + + void save(AbstractEntity.Repo repo, T migrate) { + } + } + + var helper = new Helper(); + var yield = new HashSet(); + + log.info("Checking if DB needs migration"); + + // migrate server.agent fields + servers.saveAll(entityManager.createQuery("SELECT s FROM Server s WHERE s.agent = null", Server.class) + .getResultList().stream() + .peek(srv -> agents.findForServer(srv.getId()).ifPresentOrElse( + srv::setAgent, + () -> log.warn("Could not migrate " + srv + "s Agent ID. Please set Agent ID manually (Server ID: " + srv.getId() + ")") + )) + .peek(yield::add) + .toList()); + + // migrate servers to use modules + Streams.of(servers.findMigrationCandidates(0)) + .peek(server -> { + //todo: complete migration code + // StatusModule.Factory, + helper.getOrMigrate(server, ModulePrototype.Type.Status, + () -> new StatusModulePrototype() + .setServer(server)); + // LocalFileModule.Factory, + helper.getOrMigrate(server, ModulePrototype.Type.LocalFile, + () -> new LocalFileModulePrototype() + .setDirectory(server.getDirectory()) + .setBackupsDir(server.getShConnection().getBackupsDir()) + .setForceCustomJar(server.isForceCustomJar()) + .setServer(server)); + // UptimeModule.Factory, + helper.getOrMigrate(server, ModulePrototype.Type.Uptime, + () -> new UptimeModulePrototype() + .setServer(server)); + // LocalExecutionModule.Factory, + helper.getOrMigrate(server, ModulePrototype.Type.LocalExecution, + ()->new LocalExecutionModulePrototype() + .setRamGB(server.getRamGB()) + .setCustomCommand(server.getCustomCommand()) + .setServer(server)); + // //todo: fix BackupModule.Factory, + helper.getOrMigrate(server, ModulePrototype.Type.Backup, + () -> new BackupModulePrototype() + .setEnabled(false) + .setServer(server)); + // ConsolePlayerEventModule.Factory, + helper.getOrMigrate(server, ModulePrototype.Type.ConsolePlayerEvent, + () -> new ConsolePlayerEventModulePrototype() + .setServer(server)); + // DiscordModule.Factory + helper.getOrMigrate(server, ModulePrototype.Type.Discord, + () -> new DiscordModulePrototype() + .setDiscordBot(server.getDiscordBot()) + .setPublicChannelId(server.getPublicChannelId()) + .setPublicChannelWebhook(server.getPublicChannelWebhook()) + .setPublicChannelEvents(server.getPublicChannelEvents()) + .setModerationChannelId(server.getModerationChannelId()) + .setConsoleChannelId(server.getConsoleChannelId()) + .setConsoleChannelPrefix(server.getConsoleChannelPrefix()) + .setFancyConsole(server.isFancyConsole()) + .setServer(server)); + + server.setVersion(AbstractEntity.CurrentVersion); + }) + .peek(servers::save) + .forEach(yield::add); + + if (!yield.isEmpty()) + log.info("Migrated entities:"+yield.stream() + .map(Objects::toString) + .collect(Collectors.joining("\n\t- ","\n\t- ",""))); + + return yield; + } + public static String wrapHostname(String hostname) { return "http%s://%s%s".formatted(Debug.isDebug() ? "" : "s", hostname, Debug.isDebug() ? ":42064" : ""); } @@ -140,5 +291,10 @@ public AbstractEntity findEntity(String type, UUID id) { default -> throw new BadRequestException("unknown type: " + type); }; } + + @ApiStatus.Internal + public List servers() { + return ApplicationContextProvider., List>bean(List.class, "servers"); + } } diff --git a/src/core/main/java/org/comroid/mcsd/core/ServerManager.java b/src/core/main/java/org/comroid/mcsd/core/ServerManager.java index 2b45f3bf..729b1c1d 100644 --- a/src/core/main/java/org/comroid/mcsd/core/ServerManager.java +++ b/src/core/main/java/org/comroid/mcsd/core/ServerManager.java @@ -2,62 +2,166 @@ import lombok.Value; import lombok.extern.java.Log; -import org.comroid.api.Component; -import org.comroid.api.SupplierX; -import org.comroid.api.ThrowingConsumer; -import org.comroid.api.ThrowingFunction; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.api.*; +import org.comroid.mcsd.core.entity.module.ModulePrototype; +import org.comroid.mcsd.core.entity.server.Server; import org.comroid.mcsd.core.module.ServerModule; -import org.comroid.mcsd.core.repo.ServerRepo; +import org.comroid.mcsd.core.repo.module.ModuleRepo; +import org.comroid.mcsd.core.repo.server.ServerRepo; import org.comroid.util.Streams; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Service; import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import static java.util.function.Predicate.not; @Log @Service +@DependsOn("migrateEntities") public class ServerManager { + public static final Duration TickRate = Duration.ofSeconds(30); private final Map cache = new ConcurrentHashMap<>(); private @Autowired ServerRepo servers; - private @Autowired List> serverModuleFactories; + private @Autowired ModuleRepo moduleRepo; public void startAll(List servers) { servers.stream() .map(ThrowingFunction.logging(log, srv -> get(srv.getId()).assertion("Could not initialize " + srv))) .flatMap(Streams.yield(Objects::nonNull, $ -> log.severe("A server was not initialized correctly"))) - .map(Entry::getTree) - .forEach(tree -> tree.execute(Executors.newScheduledThreadPool(4), Duration.ofSeconds(30))); + .map(entry -> { + var c = entry.reloadModules(); + return "%s loaded %d modules".formatted(entry.server, c); + }) + .forEach(log::info); + } + + public SupplierX get(Server server) { + return get(server.getId()); } - public SupplierX get(UUID id) { + public SupplierX get(final UUID id) { if (cache.containsKey(id)) return () -> cache.get(id); var result = servers.findById(id); if (result.isEmpty()) return SupplierX.empty(); final var server = result.get(); - return SupplierX.of(cache.computeIfAbsent(id, k -> { - final var modules = serverModuleFactories.stream() - .map(factory -> factory.create(server)) - .toArray(); - return new Entry(server, new Component.Base(server.getBestName(), modules)); - })); + return SupplierX.of(cache.computeIfAbsent(id, k -> new Entry(server))); } public Component tree(Server server) { - return get(server.getId()).assertion(server + " not initialized").tree; + return get(server.getId()).assertion(server + " not initialized"); } @Value - public static class Entry { + public class Entry extends Component.Base { Server server; - Component tree; + Map> tree = new ConcurrentHashMap<>(); + ScheduledExecutorService executor = Executors.newScheduledThreadPool(4, new ThreadFactory() { + public final AtomicInteger count = new AtomicInteger(0); + @Override + public Thread newThread(@NotNull Runnable r) { + return new Thread(r, "server_" + server.getId() + "_exec_"+count.incrementAndGet()); + } + }); + AtomicReference running = new AtomicReference<>(); + + @Override + public Object addChildren(Object @NotNull ... children) { + for (Object child : children) { + if (child instanceof ServerModule) { + var key = ((ServerModule) child).getProto(); + tree.put(key, Polyfill.uncheckedCast(child)); + }else super.addChildren(child); + } + return this; + } + + @Override + public int removeChildren(Object @NotNull ... children) { + var c = 0; + for (Object child : children) { + ModulePrototype key = null; + if (child instanceof ModulePrototype) + key = ((ModulePrototype) child); + else if (child instanceof ServerModule) + key = ((ServerModule) child).getProto(); + if (key != null) { + var old = tree.remove(key); + old.terminate(); + c++; + } else c += super.removeChildren(child); + } + return c; + } + + @Override + public void clearChildren() { + super.clearChildren(); + tree.clear(); + } + + @Override + public Stream streamOwnChildren() { + return tree.values().stream().map(Polyfill::uncheckedCast); + } + + /** + * Terminates and removes all modules that are no longer in DB + */ + public long cleanupModules() { + var existing = Streams.of(moduleRepo.findAllByServerId(server.getId())).toList(); + var missing = tree.keySet().stream() + .filter(not(existing::contains)) + .toList(); + return missing.stream() + .filter(proto->removeChildren(proto)>1) + .count(); + } + + public long reloadProtos() { + return 0; //todo + } + + /** + * Loads all modules that are in DB but are not loaded as a module + */ + public long refreshModules() { + return Streams.of(moduleRepo.findAllByServerId(server.getId())) + .filter(not(tree::containsKey)) + .>map(proto -> { + log.fine("Loading proto " + proto); + var module = proto.toModule(server); + module.setParent(this); + addChildren(module); + return module; + }) + .peek(this::addChildren) + .count(); + } + + /** + * Terminates the entire tree and reloads all modules from scratch + */ + public long reloadModules() { + terminate(); + var running = this.running.get(); + if (running!=null) + running.close(); + clearChildren(); + + var count = refreshModules(); + running = execute(executor, TickRate); + this.running.set(running); + return count; + } } } diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/AbstractEntity.java b/src/core/main/java/org/comroid/mcsd/core/entity/AbstractEntity.java index c674bd04..820e8054 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/AbstractEntity.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/AbstractEntity.java @@ -8,23 +8,29 @@ import org.comroid.api.BitmaskAttribute; import org.comroid.api.Named; import org.comroid.api.SupplierX; +import org.comroid.mcsd.core.entity.system.User; import org.comroid.mcsd.core.exception.InsufficientPermissionsException; import org.comroid.mcsd.util.Utils; import org.comroid.util.Bitmask; import org.comroid.util.Constraint; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import java.util.Arrays; import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.function.Predicate; @Data @Slf4j @Entity @Inheritance(strategy = InheritanceType.JOINED) public abstract class AbstractEntity implements Named { + public static final int CurrentVersion = 1; @Id private UUID id = UUID.randomUUID(); @Setter @@ -39,6 +45,7 @@ public abstract class AbstractEntity implements Named { private User owner; @ElementCollection(fetch = FetchType.EAGER) private Map permissions; + private @Nullable Integer version = CurrentVersion; public String getBestName() { return Optional.ofNullable(displayName) @@ -46,6 +53,7 @@ public String getBestName() { .or(() -> Optional.ofNullable(owner) .map(AbstractEntity::getBestName) .map(n -> n + "s " + getClass().getSimpleName())) + .filter(Predicate.not("null"::equals)) .orElseGet(id::toString); } @@ -79,7 +87,7 @@ public String getAlternateName() { } public String toString() { - return getClass().getSimpleName() + ' ' + getName(); + return getClass().getSimpleName() + ' ' + getBestName(); } public final boolean equals(Object other) { @@ -148,4 +156,9 @@ public String toString() { return "%s(0x%x)".formatted(name(),value); } } + + public interface Repo extends CrudRepository { + @Query("SELECT e FROM #{#entityName} e WHERE e.version = null OR e.version <= :version") + Iterable findMigrationCandidates(@Param("version") int fromVersion); + } } diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/FileModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/FileModulePrototype.java new file mode 100644 index 00000000..499d48e9 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/FileModulePrototype.java @@ -0,0 +1,22 @@ +package org.comroid.mcsd.core.entity.module; + +import jakarta.persistence.Basic; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.ColumnDefault; +import org.jetbrains.annotations.Nullable; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public abstract class FileModulePrototype extends ModulePrototype { + private @Nullable String directory = "~/minecraft"; + private @Nullable boolean forceCustomJar = false; + private @Nullable String backupsDir = "$HOME/backups"; +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/ModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/ModulePrototype.java new file mode 100644 index 00000000..cde863c3 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/ModulePrototype.java @@ -0,0 +1,115 @@ +package org.comroid.mcsd.core.entity.module; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import lombok.*; +import org.comroid.api.Invocable; +import org.comroid.api.Named; +import org.comroid.api.Polyfill; +import org.comroid.api.SupplierX; +import org.comroid.mcsd.core.MCSD; +import org.comroid.mcsd.core.entity.AbstractEntity; +import org.comroid.mcsd.core.entity.module.console.McsdCommandModulePrototype; +import org.comroid.mcsd.core.entity.module.discord.DiscordModulePrototype; +import org.comroid.mcsd.core.entity.module.local.LocalExecutionModulePrototype; +import org.comroid.mcsd.core.entity.module.local.LocalFileModulePrototype; +import org.comroid.mcsd.core.entity.module.local.LocalShellModulePrototype; +import org.comroid.mcsd.core.entity.module.player.ConsolePlayerEventModulePrototype; +import org.comroid.mcsd.core.entity.module.player.PlayerListModulePrototype; +import org.comroid.mcsd.core.entity.module.ssh.SshFileModulePrototype; +import org.comroid.mcsd.core.entity.module.status.BackupModulePrototype; +import org.comroid.mcsd.core.entity.module.status.StatusModulePrototype; +import org.comroid.mcsd.core.entity.module.status.UpdateModulePrototype; +import org.comroid.mcsd.core.entity.module.status.UptimeModulePrototype; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.module.ServerModule; +import org.comroid.mcsd.core.module.console.McsdCommandModule; +import org.comroid.mcsd.core.module.discord.DiscordModule; +import org.comroid.mcsd.core.module.local.LocalExecutionModule; +import org.comroid.mcsd.core.module.local.LocalFileModule; +import org.comroid.mcsd.core.module.local.LocalShellModule; +import org.comroid.mcsd.core.module.player.ConsolePlayerEventModule; +import org.comroid.mcsd.core.module.player.PlayerListModule; +import org.comroid.mcsd.core.module.ssh.SshFileModule; +import org.comroid.mcsd.core.module.status.BackupModule; +import org.comroid.mcsd.core.module.status.StatusModule; +import org.comroid.mcsd.core.module.status.UpdateModule; +import org.comroid.mcsd.core.module.status.UptimeModule; +import org.comroid.mcsd.core.repo.module.ModuleRepo; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; + +import static org.comroid.mcsd.core.util.ApplicationContextProvider.bean; +import static org.comroid.util.StackTraceUtils.lessSimpleName; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"server_id", "dtype"})) +public abstract class ModulePrototype extends AbstractEntity { + private @ManyToOne Server server; + private String dtype = Type.of(this).require(Enum::name, "Unimplemented type: " + lessSimpleName(getClass())); + private boolean enabled = true; + + @Override + public @Nullable String getDisplayName() { + return Objects.requireNonNull(super.getDisplayName(), ()->dtype+" for "+server); + } + + public , P extends ModulePrototype> T toModule(Server server) { + var type = Type.valueOf(dtype); + if (!type.proto.isAssignableFrom(getClass())) + throw new RuntimeException("Invalid dtype " + dtype + " for module " + this); + var module = type.ctor.autoInvoke(server, this); + return Polyfill.uncheckedCast(module); + } + + @Getter + public enum Type implements Named { + // console + McsdCommand("MCSD Command from Console", McsdCommandModule.class, McsdCommandModulePrototype.class, MCSD::getModules_mcsd), // discord + Discord("Discord Integration from Console", DiscordModule.class, DiscordModulePrototype.class, MCSD::getModules_discord), // local + LocalExecution("Local Execution Module", LocalExecutionModule.class, LocalExecutionModulePrototype.class, MCSD::getModules_localExecution), + LocalFile("Local File Module", LocalFileModule.class, LocalFileModulePrototype.class, MCSD::getModules_localFiles), + LocalShell("Local Shell Execution Module", LocalShellModule.class, LocalShellModulePrototype.class, MCSD::getModules_localShell), // player + ConsolePlayerEvent("Forward Console Player Events", ConsolePlayerEventModule.class, ConsolePlayerEventModulePrototype.class, MCSD::getModules_consolePlayerEvents), + PlayerList("Cache Player List from Player Events", PlayerListModule.class, PlayerListModulePrototype.class, MCSD::getModules_playerList), // ssh + SshFile("SSH File Module", SshFileModule.class, SshFileModulePrototype.class, MCSD::getModules_sshFile), //status + Backup("Automated Backups", BackupModule.class, BackupModulePrototype.class, MCSD::getModules_backup), + Update("Automated Updates", UpdateModule.class, UpdateModulePrototype.class, MCSD::getModules_update), + Status("Internal Status Logging", StatusModule.class, StatusModulePrototype.class, MCSD::getModules_status), + Uptime("Internal Uptime Logging", UptimeModule.class, UptimeModulePrototype.class, MCSD::getModules_uptime); + + private final String description; + private final Class> impl; + private final Class proto; + private final Function> repo; + private final Invocable> ctor; + + Type(String description, Class> impl, Class proto, Function> repo) { + this.description = description; + this.impl = impl; + this.proto = proto; + this.repo = repo; + this.ctor = Invocable.ofConstructor(impl, Server.class, proto); + } + + @Override + public String getAlternateName() { + return description; + } + + public static SupplierX of(ModulePrototype proto) { + return SupplierX.ofOptional(Stream.of(values()) + .filter(type -> type.proto.isInstance(proto)) + .findAny()); + } + } +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/ShellModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/ShellModulePrototype.java new file mode 100644 index 00000000..66e5a6a1 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/ShellModulePrototype.java @@ -0,0 +1,15 @@ +package org.comroid.mcsd.core.entity.module; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public abstract class ShellModulePrototype extends ModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/console/ConsoleModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/console/ConsoleModulePrototype.java new file mode 100644 index 00000000..115291b3 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/console/ConsoleModulePrototype.java @@ -0,0 +1,14 @@ +package org.comroid.mcsd.core.entity.module.console; + +import jakarta.persistence.Entity; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.java.Log; +import org.comroid.mcsd.core.entity.module.ModulePrototype; + +@Log +@Getter +@Setter +@Entity +public abstract class ConsoleModulePrototype extends ModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/console/McsdCommandModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/console/McsdCommandModulePrototype.java new file mode 100644 index 00000000..a0cf04d7 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/console/McsdCommandModulePrototype.java @@ -0,0 +1,18 @@ +package org.comroid.mcsd.core.entity.module.console; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.java.Log; +import org.comroid.mcsd.core.entity.module.ModulePrototype; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public class McsdCommandModulePrototype extends ModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/discord/DiscordModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/discord/DiscordModulePrototype.java new file mode 100644 index 00000000..1cc59d88 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/discord/DiscordModulePrototype.java @@ -0,0 +1,28 @@ +package org.comroid.mcsd.core.entity.module.discord; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.ModulePrototype; +import org.comroid.mcsd.core.entity.system.DiscordBot; +import org.jetbrains.annotations.Nullable; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class DiscordModulePrototype extends ModulePrototype { + private @Nullable @ManyToOne DiscordBot discordBot; + private @Nullable String publicChannelWebhook; + private @Nullable @Column(unique = true) Long publicChannelId; + private @Nullable Long moderationChannelId; + private @Nullable @Column(unique = true) Long consoleChannelId; + private @Nullable String consoleChannelPrefix; + private @Nullable Integer publicChannelEvents = 0xFFFF_FFFF; + private @Nullable Boolean fancyConsole = true; +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalExecutionModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalExecutionModulePrototype.java new file mode 100644 index 00000000..d2f082ed --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalExecutionModulePrototype.java @@ -0,0 +1,21 @@ +package org.comroid.mcsd.core.entity.module.local; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.java.Log; +import org.comroid.mcsd.core.entity.module.console.ConsoleModulePrototype; +import org.jetbrains.annotations.Nullable; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class LocalExecutionModulePrototype extends ConsoleModulePrototype { + private @Nullable @Column(columnDefinition = "TEXT") String customCommand = null; + private @Nullable Byte ramGB = 4; +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalFileModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalFileModulePrototype.java new file mode 100644 index 00000000..08743518 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalFileModulePrototype.java @@ -0,0 +1,17 @@ +package org.comroid.mcsd.core.entity.module.local; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.java.Log; +import org.comroid.mcsd.core.entity.module.FileModulePrototype; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public class LocalFileModulePrototype extends FileModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalShellModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalShellModulePrototype.java new file mode 100644 index 00000000..56a02b9c --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/local/LocalShellModulePrototype.java @@ -0,0 +1,17 @@ +package org.comroid.mcsd.core.entity.module.local; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.java.Log; +import org.comroid.mcsd.core.entity.module.ShellModulePrototype; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public class LocalShellModulePrototype extends ShellModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/player/ConsolePlayerEventModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/player/ConsolePlayerEventModulePrototype.java new file mode 100644 index 00000000..5b3f49b2 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/player/ConsolePlayerEventModulePrototype.java @@ -0,0 +1,16 @@ +package org.comroid.mcsd.core.entity.module.player; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.extern.java.Log; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public class ConsolePlayerEventModulePrototype extends PlayerEventModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/player/PlayerEventModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/player/PlayerEventModulePrototype.java new file mode 100644 index 00000000..5621e9b8 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/player/PlayerEventModulePrototype.java @@ -0,0 +1,41 @@ +package org.comroid.mcsd.core.entity.module.player; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.ModulePrototype; +import org.comroid.mcsd.core.module.console.ConsoleModule; +import org.jetbrains.annotations.Nullable; + +import java.util.regex.Pattern; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public abstract class PlayerEventModulePrototype extends ModulePrototype { + private @Nullable @Basic Pattern chatPattern = ConsoleModule.pattern( + "([(\\[{<](?[\\w\\s_-]+)[>}\\])]\\s?)*" + + //"([(\\[{<]" + + "<" + + "(?[\\w\\S_-]+)" + + ">\\s?" + + //"[>}\\])]\\s?)\\s?" + + "([(\\[{<](?[\\w\\s_-]+)[>}\\])]\\s?)*" + + "(?.+)\\r?\\n?.*"); + private @Nullable Pattern broadcastPattern = ConsoleModule.pattern( + "(?[\\S\\w_-]+) issued parent command: " + + "/(?(me)|(say)|(broadcast)) " + + "(?.+)\\r?\\n?.*"); + private @Nullable Pattern joinLeavePattern = ConsoleModule.pattern( + "(?[\\S\\w_-]+) " + + "(?(joined|left) the game)\\r?\\n?"); + private @Nullable Pattern achievementPattern = ConsoleModule.pattern( + "(?[\\S\\w_-]+) " + + "(?has (made the advancement|completed the challenge) " + + "(\\[(?[\\w\\s]+)]))\\r?\\n?"); +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/player/PlayerListModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/player/PlayerListModulePrototype.java new file mode 100644 index 00000000..8f816641 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/player/PlayerListModulePrototype.java @@ -0,0 +1,16 @@ +package org.comroid.mcsd.core.entity.module.player; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.ModulePrototype; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public class PlayerListModulePrototype extends ModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/ssh/SshFileModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/ssh/SshFileModulePrototype.java new file mode 100644 index 00000000..dc56946e --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/ssh/SshFileModulePrototype.java @@ -0,0 +1,20 @@ +package org.comroid.mcsd.core.entity.module.ssh; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.FileModulePrototype; +import org.comroid.mcsd.core.entity.system.ShConnection; +import org.jetbrains.annotations.NotNull; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class SshFileModulePrototype extends FileModulePrototype { + private @NotNull @ManyToOne ShConnection shConnection; +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/status/BackupModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/BackupModulePrototype.java new file mode 100644 index 00000000..0903e831 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/BackupModulePrototype.java @@ -0,0 +1,22 @@ +package org.comroid.mcsd.core.entity.module.status; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.ModulePrototype; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.time.Instant; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class BackupModulePrototype extends ModulePrototype { + private @Nullable Duration backupPeriod = Duration.ofHours(12); + private @Nullable Instant lastBackup = Instant.ofEpochMilli(0); +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/status/StatusModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/StatusModulePrototype.java new file mode 100644 index 00000000..0154c756 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/StatusModulePrototype.java @@ -0,0 +1,16 @@ +package org.comroid.mcsd.core.entity.module.status; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.ModulePrototype; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public class StatusModulePrototype extends ModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/status/UpdateModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/UpdateModulePrototype.java new file mode 100644 index 00000000..958177a6 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/UpdateModulePrototype.java @@ -0,0 +1,23 @@ +package org.comroid.mcsd.core.entity.module.status; + +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.ModulePrototype; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.time.Instant; + +@Entity +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class UpdateModulePrototype extends ModulePrototype { + private @Nullable @Basic Duration updatePeriod = Duration.ofDays(7); + private @Nullable Instant lastUpdate = Instant.ofEpochMilli(0); +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/module/status/UptimeModulePrototype.java b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/UptimeModulePrototype.java new file mode 100644 index 00000000..7304a682 --- /dev/null +++ b/src/core/main/java/org/comroid/mcsd/core/entity/module/status/UptimeModulePrototype.java @@ -0,0 +1,16 @@ +package org.comroid.mcsd.core.entity.module.status; + +import jakarta.persistence.Entity; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.comroid.mcsd.core.entity.module.ModulePrototype; + +@Entity +@Getter +@Setter +@NoArgsConstructor +//@AllArgsConstructor +public class UptimeModulePrototype extends ModulePrototype { +} diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/Backup.java b/src/core/main/java/org/comroid/mcsd/core/entity/server/Backup.java similarity index 81% rename from src/core/main/java/org/comroid/mcsd/core/entity/Backup.java rename to src/core/main/java/org/comroid/mcsd/core/entity/server/Backup.java index 588e7b90..9dfce27f 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/Backup.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/server/Backup.java @@ -1,8 +1,9 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.server; import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; import lombok.*; +import org.comroid.mcsd.core.entity.AbstractEntity; import java.time.Duration; import java.time.Instant; diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/Server.java b/src/core/main/java/org/comroid/mcsd/core/entity/server/Server.java similarity index 81% rename from src/core/main/java/org/comroid/mcsd/core/entity/Server.java rename to src/core/main/java/org/comroid/mcsd/core/entity/server/Server.java index 5dd31283..9b6dcad8 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/Server.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/server/Server.java @@ -1,4 +1,4 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.server; import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.rmmccann.minecraft.status.query.MCQuery; @@ -11,6 +11,12 @@ import org.comroid.mcsd.api.dto.StatusMessage; import org.comroid.mcsd.api.model.Status; import org.comroid.mcsd.core.ServerManager; +import org.comroid.mcsd.core.entity.AbstractEntity; +import org.comroid.mcsd.core.entity.module.FileModulePrototype; +import org.comroid.mcsd.core.entity.system.Agent; +import org.comroid.mcsd.core.entity.system.DiscordBot; +import org.comroid.mcsd.core.entity.system.ShConnection; +import org.comroid.mcsd.core.module.FileModule; import org.comroid.mcsd.core.module.ServerModule; import org.comroid.util.Token; import org.intellij.lang.annotations.Language; @@ -39,24 +45,12 @@ public class Server extends AbstractEntity { public static final Duration statusCacheLifetime = Duration.ofMinutes(1); public static final Duration statusTimeout = Duration.ofSeconds(10); private static final Duration TickRate = Duration.ofMinutes(1); - private @ManyToOne ShConnection shConnection; - private @ManyToOne @Nullable DiscordBot discordBot; private @Nullable String homepage; - private @Nullable String PublicChannelWebhook; - private @Nullable @Column(unique = true) Long PublicChannelId; - private @Nullable Long ModerationChannelId; - private @Nullable @Column(unique = true) Long ConsoleChannelId; - private @Nullable String ConsoleChannelPrefix; - private int publicChannelEvents = 0xFFFF_FFFF; - private boolean fancyConsole = true; - private boolean forceCustomJar = false; - private @Nullable @Column(columnDefinition = "TEXT") String customCommand = null; private String mcVersion = "1.19.4"; private String host; private int port = 25565; - private String directory = "~/minecraft"; + private @Deprecated String directory = "~/minecraft"; private Mode mode = Mode.Paper; - private byte ramGB = 4; private boolean enabled = false; private boolean managed = false; private boolean whitelist = false; @@ -65,11 +59,24 @@ public class Server extends AbstractEntity { private int queryPort = 25565; private int rConPort = Defaults.RCON_PORT; private @Getter(onMethod = @__(@JsonIgnore)) String rConPassword = Token.random(16, false); - private @Nullable Duration backupPeriod = Duration.ofHours(12); - private Duration updatePeriod = Duration.ofDays(7); - private Instant lastBackup = Instant.ofEpochMilli(0); - private Instant lastUpdate = Instant.ofEpochMilli(0); private @ElementCollection(fetch = FetchType.EAGER) List tickerMessages; + private @Nullable @ManyToOne Agent agent; // todo: make not nullable + private @Deprecated @ManyToOne ShConnection shConnection; + private @Deprecated @ManyToOne @Nullable DiscordBot discordBot; + private @Deprecated @Nullable String PublicChannelWebhook; + private @Deprecated @Nullable @Column(unique = true) Long PublicChannelId; + private @Deprecated @Nullable Long ModerationChannelId; + private @Deprecated @Nullable @Column(unique = true) Long ConsoleChannelId; + private @Deprecated @Nullable String ConsoleChannelPrefix; + private @Deprecated int publicChannelEvents = 0xFFFF_FFFF; + private @Deprecated boolean fancyConsole = true; + private @Deprecated boolean forceCustomJar = false; + private @Deprecated @Nullable @Column(columnDefinition = "TEXT") String customCommand = null; + private @Deprecated byte ramGB = 4; + private @Deprecated @Nullable Duration backupPeriod = Duration.ofHours(12); + private @Deprecated Instant lastBackup = Instant.ofEpochMilli(0); + private @Deprecated @Nullable Duration updatePeriod = Duration.ofDays(7); + private @Deprecated Instant lastUpdate = Instant.ofEpochMilli(0); @JsonIgnore public boolean isVanilla() { @@ -145,7 +152,7 @@ public String getLoaderName() { } public Path path(String... extra) { - return Paths.get(getDirectory(), extra); + return Paths.get(((FileModulePrototype) component(FileModule.class).assertion().getProto()).getDirectory(), extra); } @SneakyThrows @@ -206,18 +213,12 @@ public CompletableFuture status() { .completeOnTimeout(new StatusMessage(getId()), (long) (statusTimeout.toSeconds() * 1.5), TimeUnit.SECONDS); } - @Deprecated - public Optional shCon() { - return Optional.ofNullable(shConnection); - } - - public SupplierX component(Class type) { + public > SupplierX component(Class type) { return SupplierX.ofStream(components(type)); } - public Stream components(Class type) { + public > Stream components(Class type) { return bean(ServerManager.class).get(getId()) .assertion(this+" not initialized") - .getTree() .components(type); } diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/ServerUptimeEntry.java b/src/core/main/java/org/comroid/mcsd/core/entity/server/ServerUptimeEntry.java similarity index 91% rename from src/core/main/java/org/comroid/mcsd/core/entity/ServerUptimeEntry.java rename to src/core/main/java/org/comroid/mcsd/core/entity/server/ServerUptimeEntry.java index 749aa46a..58594904 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/ServerUptimeEntry.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/server/ServerUptimeEntry.java @@ -1,9 +1,8 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.server; import jakarta.persistence.*; import lombok.*; import org.comroid.mcsd.api.model.Status; -import org.jetbrains.annotations.Nullable; import java.time.Instant; diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/Agent.java b/src/core/main/java/org/comroid/mcsd/core/entity/system/Agent.java similarity index 87% rename from src/core/main/java/org/comroid/mcsd/core/entity/Agent.java rename to src/core/main/java/org/comroid/mcsd/core/entity/system/Agent.java index 64b9d6c3..4d8a201d 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/Agent.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/system/Agent.java @@ -1,8 +1,9 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.system; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import lombok.*; +import org.comroid.mcsd.core.entity.AbstractEntity; import org.comroid.util.Token; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/AuthorizationLink.java b/src/core/main/java/org/comroid/mcsd/core/entity/system/AuthorizationLink.java similarity index 88% rename from src/core/main/java/org/comroid/mcsd/core/entity/AuthorizationLink.java rename to src/core/main/java/org/comroid/mcsd/core/entity/system/AuthorizationLink.java index 1391c16c..15029e9e 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/AuthorizationLink.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/system/AuthorizationLink.java @@ -1,12 +1,11 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.system; import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; -import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.RequiredArgsConstructor; +import org.comroid.mcsd.core.entity.system.User; import java.time.Duration; import java.time.Instant; diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/DiscordBot.java b/src/core/main/java/org/comroid/mcsd/core/entity/system/DiscordBot.java similarity index 82% rename from src/core/main/java/org/comroid/mcsd/core/entity/DiscordBot.java rename to src/core/main/java/org/comroid/mcsd/core/entity/system/DiscordBot.java index 2647305e..a14efa79 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/DiscordBot.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/system/DiscordBot.java @@ -1,4 +1,4 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.system; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.Basic; @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.comroid.mcsd.core.entity.AbstractEntity; import org.jetbrains.annotations.Nullable; @Getter diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/ShConnection.java b/src/core/main/java/org/comroid/mcsd/core/entity/system/ShConnection.java similarity index 79% rename from src/core/main/java/org/comroid/mcsd/core/entity/ShConnection.java rename to src/core/main/java/org/comroid/mcsd/core/entity/system/ShConnection.java index 408b9bed..22f2bc34 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/ShConnection.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/system/ShConnection.java @@ -1,4 +1,4 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.system; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.Setter; import org.comroid.api.BitmaskAttribute; +import org.comroid.mcsd.core.entity.AbstractEntity; import org.comroid.util.Bitmask; import java.util.UUID; @@ -15,12 +16,12 @@ @Setter @Entity public class ShConnection extends AbstractEntity { - private String host; + private @Basic String host; private int port = 22; private String username; private @Getter(onMethod = @__(@JsonIgnore)) String password; - private String backupsDir = "$HOME/backups"; private int capabilites = Bitmask.combine(Capability.SSH); + private @Deprecated String backupsDir = "$HOME/backups"; @Override public String toString() { diff --git a/src/core/main/java/org/comroid/mcsd/core/entity/User.java b/src/core/main/java/org/comroid/mcsd/core/entity/system/User.java similarity index 98% rename from src/core/main/java/org/comroid/mcsd/core/entity/User.java rename to src/core/main/java/org/comroid/mcsd/core/entity/system/User.java index 4f536ccf..b299b59a 100644 --- a/src/core/main/java/org/comroid/mcsd/core/entity/User.java +++ b/src/core/main/java/org/comroid/mcsd/core/entity/system/User.java @@ -1,4 +1,4 @@ -package org.comroid.mcsd.core.entity; +package org.comroid.mcsd.core.entity.system; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; @@ -6,6 +6,7 @@ import lombok.extern.java.Log; import org.comroid.api.BitmaskAttribute; import org.comroid.api.SupplierX; +import org.comroid.mcsd.core.entity.AbstractEntity; import org.comroid.mcsd.core.module.discord.DiscordAdapter; import org.comroid.util.Cache; import org.comroid.util.Constraint; diff --git a/src/core/main/java/org/comroid/mcsd/core/exception/InsufficientPermissionsException.java b/src/core/main/java/org/comroid/mcsd/core/exception/InsufficientPermissionsException.java index 98c03281..d7b9663f 100644 --- a/src/core/main/java/org/comroid/mcsd/core/exception/InsufficientPermissionsException.java +++ b/src/core/main/java/org/comroid/mcsd/core/exception/InsufficientPermissionsException.java @@ -1,7 +1,7 @@ package org.comroid.mcsd.core.exception; import org.comroid.mcsd.core.entity.AbstractEntity; -import org.comroid.mcsd.core.entity.User; +import org.comroid.mcsd.core.entity.system.User; import org.jetbrains.annotations.Nullable; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; diff --git a/src/core/main/java/org/comroid/mcsd/core/model/DiscordMessageSource.java b/src/core/main/java/org/comroid/mcsd/core/model/DiscordMessageSource.java index cd6038d4..692c9ca0 100644 --- a/src/core/main/java/org/comroid/mcsd/core/model/DiscordMessageSource.java +++ b/src/core/main/java/org/comroid/mcsd/core/model/DiscordMessageSource.java @@ -3,7 +3,7 @@ import lombok.Data; import net.dv8tion.jda.api.EmbedBuilder; import org.comroid.api.StreamSupplier; -import org.comroid.mcsd.core.entity.User; +import org.comroid.mcsd.core.entity.system.User; import org.comroid.util.Streams; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/src/core/main/java/org/comroid/mcsd/core/model/ServerHolder.java b/src/core/main/java/org/comroid/mcsd/core/model/ServerHolder.java index ceef9770..1683a604 100644 --- a/src/core/main/java/org/comroid/mcsd/core/model/ServerHolder.java +++ b/src/core/main/java/org/comroid/mcsd/core/model/ServerHolder.java @@ -1,6 +1,6 @@ package org.comroid.mcsd.core.model; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.mcsd.core.entity.server.Server; public interface ServerHolder { Server getServer(); diff --git a/src/core/main/java/org/comroid/mcsd/core/module/FileModule.java b/src/core/main/java/org/comroid/mcsd/core/module/FileModule.java index b95f5f94..e5dec57b 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/FileModule.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/FileModule.java @@ -2,7 +2,8 @@ import lombok.SneakyThrows; import org.comroid.api.io.FileHandle; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.module.FileModulePrototype; import org.comroid.util.JSON; import org.comroid.util.MD5; @@ -13,9 +14,9 @@ import java.util.Objects; import java.util.Properties; -public abstract class FileModule extends ServerModule { - public FileModule(Server parent) { - super(parent); +public abstract class FileModule extends ServerModule { + public FileModule(Server server, T proto) { + super(server, proto); } public abstract boolean mkDir(String path); @@ -26,7 +27,7 @@ public FileModule(Server parent) { @SneakyThrows public boolean isJarUpToDate() { - if (server.isForceCustomJar()) + if (proto.isForceCustomJar()) return true; var serverJar = new FileHandle(server.path("server.jar").toFile()); if (!serverJar.exists()) diff --git a/src/core/main/java/org/comroid/mcsd/core/module/ServerModule.java b/src/core/main/java/org/comroid/mcsd/core/module/ServerModule.java index 57a15b89..9519e24a 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/ServerModule.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/ServerModule.java @@ -1,31 +1,36 @@ package org.comroid.mcsd.core.module; +import lombok.AllArgsConstructor; import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; import org.comroid.api.Component; import org.comroid.api.Named; import org.comroid.mcsd.core.ServerManager; -import org.comroid.mcsd.core.entity.Server; -import org.comroid.mcsd.core.util.ApplicationContextProvider; +import org.comroid.mcsd.core.entity.module.ModulePrototype; +import org.comroid.mcsd.core.entity.server.Server; import org.jetbrains.annotations.Nullable; import static org.comroid.mcsd.core.util.ApplicationContextProvider.bean; -public abstract class ServerModule extends Component.Base implements Named { +@Getter +@AllArgsConstructor +public abstract class ServerModule extends Component.Base implements Named { protected final Server server; + protected T proto; @Override public @Nullable Component getParent() { return bean(ServerManager.class).tree(server); } - public ServerModule(Server server) { - this.server = server; + @Override + public boolean isEnabled() { + return proto.isEnabled(); } - @Data - public static abstract class Factory { - protected final Class type; - - public abstract Module create(Server parent); + @Override + public boolean isSubComponent() { + return true; } } diff --git a/src/core/main/java/org/comroid/mcsd/core/module/ShellModule.java b/src/core/main/java/org/comroid/mcsd/core/module/ShellModule.java index db330eac..45ee222d 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/ShellModule.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/ShellModule.java @@ -1,11 +1,12 @@ package org.comroid.mcsd.core.module; import org.comroid.api.DelegateStream; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.module.ShellModulePrototype; -public abstract class ShellModule extends ServerModule { - public ShellModule(Server parent) { - super(parent); +public abstract class ShellModule extends ServerModule { + public ShellModule(Server server, T proto) { + super(server, proto); } public abstract DelegateStream.IO execute(String... command); diff --git a/src/core/main/java/org/comroid/mcsd/core/module/console/ConsoleModule.java b/src/core/main/java/org/comroid/mcsd/core/module/console/ConsoleModule.java index 8bc28a3e..092d25d1 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/console/ConsoleModule.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/console/ConsoleModule.java @@ -5,7 +5,8 @@ import lombok.experimental.FieldDefaults; import lombok.extern.java.Log; import org.comroid.api.Event; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.module.console.ConsoleModulePrototype; import org.comroid.mcsd.core.module.ServerModule; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; @@ -17,16 +18,17 @@ @Log @Getter @FieldDefaults(level = AccessLevel.PRIVATE) -public abstract class ConsoleModule extends ServerModule { +public abstract class ConsoleModule extends ServerModule { public static final Pattern McsdPattern = commandPattern("mcsd"); + + public ConsoleModule(Server server, T proto) { + super(server, proto); + } + public static Pattern commandPattern(String command) {return pattern("(?[\\S\\w_-]+) issued parent command: /"+command+" (?[\\w\\s_-]+)\\r?\\n?.*");} protected Event.Bus bus; - public ConsoleModule(Server parent) { - super(parent); - } - @Override protected void $initialize() { bus = new Event.Bus<>(); diff --git a/src/core/main/java/org/comroid/mcsd/core/module/console/McsdCommandModule.java b/src/core/main/java/org/comroid/mcsd/core/module/console/McsdCommandModule.java index 9b528875..853a0f81 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/console/McsdCommandModule.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/console/McsdCommandModule.java @@ -7,10 +7,11 @@ import lombok.extern.java.Log; import org.comroid.api.Command; import org.comroid.api.Component; -import org.comroid.mcsd.core.entity.Server; -import org.comroid.mcsd.core.entity.User; +import org.comroid.mcsd.core.entity.module.console.McsdCommandModulePrototype; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.system.User; import org.comroid.mcsd.core.module.ServerModule; -import org.comroid.mcsd.core.repo.UserRepo; +import org.comroid.mcsd.core.repo.system.UserRepo; import org.comroid.mcsd.util.McFormatCode; import org.comroid.mcsd.util.Tellraw; import org.comroid.mcsd.util.Utils; @@ -27,25 +28,17 @@ @ToString @FieldDefaults(level = AccessLevel.PRIVATE) @Component.Requires(ConsoleModule.class) -public class McsdCommandModule extends ServerModule implements Command.Handler { +public class McsdCommandModule extends ServerModule implements Command.Handler { public static final Pattern McsdPattern = ConsoleModule.commandPattern("mcsd"); - public static final Factory Factory = new Factory<>(McsdCommandModule.class) { - @Override - public McsdCommandModule create(Server parent) { - return new McsdCommandModule(parent); - } - }; - final Command.Manager cmdr = new Command.Manager(this); - ConsoleModule console; + ConsoleModule console; - protected McsdCommandModule(Server parent) { - super(parent); + public McsdCommandModule(Server server, McsdCommandModulePrototype proto) { + super(server, proto); } @Override protected void $initialize() { - ; console = server.component(ConsoleModule.class).assertion(); addChildren(Utils.listenForPattern(console.bus, McsdPattern).subscribeData(matcher -> { diff --git a/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordAdapter.java b/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordAdapter.java index 8517553e..29197405 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordAdapter.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordAdapter.java @@ -36,14 +36,15 @@ import org.comroid.api.Event; import org.comroid.api.Polyfill; import org.comroid.mcsd.api.Defaults; -import org.comroid.mcsd.core.entity.DiscordBot; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.mcsd.core.MCSD; +import org.comroid.mcsd.core.entity.system.DiscordBot; +import org.comroid.mcsd.core.entity.server.Server; import org.comroid.mcsd.core.model.DiscordMessageSource; import org.comroid.mcsd.core.module.status.BackupModule; import org.comroid.mcsd.core.module.console.ConsoleModule; import org.comroid.mcsd.core.module.status.UpdateModule; -import org.comroid.mcsd.core.repo.ServerRepo; -import org.comroid.mcsd.core.repo.UserRepo; +import org.comroid.mcsd.core.repo.server.ServerRepo; +import org.comroid.mcsd.core.repo.system.UserRepo; import org.comroid.mcsd.util.McFormatCode; import org.comroid.mcsd.util.Tellraw; import org.comroid.util.*; @@ -453,11 +454,15 @@ public CompletableFuture getWebhook(@Nullable String webhookUrl, .map(wh -> WebhookClientBuilder.fromJDA(wh).build()) .submit() .thenApply(wh -> { - final var parents = bean(ServerRepo.class); - parents.findByDiscordChannel(channelId) + var moduleRepo = bean(MCSD.class).getModules_discord(); + bean(ServerRepo.class) + .findByDiscordChannel(channelId) .stream() - .map(srv -> srv.setPublicChannelWebhook(wh.getUrl())) - .forEach(parents::save); + .flatMap(server -> moduleRepo + .findByServerIdAndDtype(server.getId(), "Discord") + .stream()) + .map(discord -> discord.setPublicChannelWebhook(wh.getUrl())) + .forEach(moduleRepo::save); return wh; })); } @@ -520,7 +525,7 @@ public void accept(DiscordMessageSource msg) { } } - private EmbedBuilder embed(@NotNull EmbedBuilder builder, @Nullable org.comroid.mcsd.core.entity.User player) { + private EmbedBuilder embed(@NotNull EmbedBuilder builder, @Nullable org.comroid.mcsd.core.entity.system.User player) { if (player != null) builder = builder.setAuthor(player.getName(), player.getNameMcURL(), player.getHeadURL()); builder.setTimestamp(Instant.now()); diff --git a/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordModule.java b/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordModule.java index adf040dd..68dcb257 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordModule.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/discord/DiscordModule.java @@ -13,15 +13,16 @@ import org.comroid.api.SupplierX; import org.comroid.api.ThrowingSupplier; import org.comroid.mcsd.api.model.IStatusMessage; -import org.comroid.mcsd.core.entity.Server; -import org.comroid.mcsd.core.entity.User; +import org.comroid.mcsd.core.entity.module.discord.DiscordModulePrototype; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.entity.system.User; import org.comroid.mcsd.core.model.DiscordMessageSource; import org.comroid.mcsd.core.module.player.ConsolePlayerEventModule; import org.comroid.mcsd.core.module.console.ConsoleModule; import org.comroid.mcsd.core.module.ServerModule; import org.comroid.mcsd.core.module.player.PlayerEventModule; import org.comroid.mcsd.core.module.status.StatusModule; -import org.comroid.mcsd.core.repo.UserRepo; +import org.comroid.mcsd.core.repo.system.UserRepo; import org.comroid.mcsd.util.Tellraw; import java.time.Instant; @@ -38,20 +39,13 @@ @Getter @ToString @Component.Requires({ConsolePlayerEventModule.class,ConsoleModule.class}) -public class DiscordModule extends ServerModule { +public class DiscordModule extends ServerModule { public static final Pattern EmojiPattern = Pattern.compile(".*:(?[\\w-_]+):?.*"); - - public static final Factory Factory = new Factory<>(DiscordModule.class) { - @Override - public DiscordModule create(Server parent) { - return new DiscordModule(parent); - } - }; protected final DiscordAdapter adapter; - public DiscordModule(Server parent) { - super(parent); - this.adapter = Optional.ofNullable(parent.getDiscordBot()) + public DiscordModule(Server server, DiscordModulePrototype proto) { + super(server, proto); + this.adapter = Optional.ofNullable(proto.getDiscordBot()) .map(DiscordAdapter::get) .orElseThrow(); } @@ -66,8 +60,8 @@ public DiscordModule(Server parent) { chat.ifBothPresent(consoleModule, (chatBus, console) -> { // public channel - Optional.ofNullable(server.getPublicChannelId()).ifPresent(id -> { - final var webhook = adapter.getWebhook(server.getPublicChannelWebhook(), id) + Optional.ofNullable(proto.getPublicChannelId()).ifPresent(id -> { + final var webhook = adapter.getWebhook(proto.getPublicChannelWebhook(), id) .thenApply(adapter::messageTemplate).join(); final var bot = adapter.messageTemplate(id); @@ -89,7 +83,7 @@ public DiscordModule(Server parent) { addChildren( // mc -> dc - chatBus.filterData(msg -> msg.getType().isFlagSet(server.getPublicChannelEvents())) + chatBus.filterData(msg -> msg.getType().isFlagSet(proto.getPublicChannelEvents())) .mapData(msg -> { var player = bean(UserRepo.class).get(msg.getUsername()).assertion(); String str = msg.toString(); @@ -141,7 +135,7 @@ public DiscordModule(Server parent) { }); //moderation channel - Optional.ofNullable(server.getModerationChannelId()).ifPresent(id -> { + Optional.ofNullable(proto.getModerationChannelId()).ifPresent(id -> { final var bot = adapter.messageTemplate(id); // public status -> dc @@ -162,8 +156,8 @@ public DiscordModule(Server parent) { }); // console channel - Optional.ofNullable(server.getConsoleChannelId()).ifPresent(id -> { - final var channel = adapter.channelAsStream(id, server.isFancyConsole()); + Optional.ofNullable(proto.getConsoleChannelId()).ifPresent(id -> { + final var channel = adapter.channelAsStream(id, proto.getFancyConsole()); addChildren( // mc -> dc console.getBus().subscribeData(channel::println), @@ -172,13 +166,13 @@ public DiscordModule(Server parent) { .filterData(msg -> !msg.getAuthor().isBot()) .mapData(msg -> { var raw = msg.getContentRaw(); - if (server.isFancyConsole() && !msg.getAuthor().equals(adapter.getJda().getSelfUser())) + if (proto.getFancyConsole() && !msg.getAuthor().equals(adapter.getJda().getSelfUser())) msg.delete().queue(); //noinspection RedundantCast //ide error return (String) raw; }) - .filterData(cmd -> server.getConsoleChannelPrefix() == null || cmd.startsWith(server.getConsoleChannelPrefix())) - .mapData(cmd -> server.getConsoleChannelPrefix() == null ? cmd : cmd.substring(server.getConsoleChannelPrefix().length())) + .filterData(cmd -> proto.getConsoleChannelPrefix() == null || cmd.startsWith(proto.getConsoleChannelPrefix())) + .mapData(cmd -> proto.getConsoleChannelPrefix() == null ? cmd : cmd.substring(proto.getConsoleChannelPrefix().length())) .subscribeData(input -> console.execute(input).exceptionally(Polyfill.exceptionLogger(log))) ); }); diff --git a/src/core/main/java/org/comroid/mcsd/core/module/local/LocalExecutionModule.java b/src/core/main/java/org/comroid/mcsd/core/module/local/LocalExecutionModule.java index 61c155f0..07eed061 100644 --- a/src/core/main/java/org/comroid/mcsd/core/module/local/LocalExecutionModule.java +++ b/src/core/main/java/org/comroid/mcsd/core/module/local/LocalExecutionModule.java @@ -11,11 +11,13 @@ import org.comroid.api.os.OS; import org.comroid.mcsd.api.model.Status; import org.comroid.mcsd.core.ServerManager; -import org.comroid.mcsd.core.entity.Server; +import org.comroid.mcsd.core.entity.module.FileModulePrototype; +import org.comroid.mcsd.core.entity.module.local.LocalExecutionModulePrototype; +import org.comroid.mcsd.core.entity.server.Server; +import org.comroid.mcsd.core.module.FileModule; import org.comroid.mcsd.core.module.console.ConsoleModule; import org.comroid.mcsd.core.module.status.StatusModule; import org.comroid.mcsd.core.module.status.UpdateModule; -import org.comroid.mcsd.core.util.ApplicationContextProvider; import org.comroid.mcsd.util.Utils; import org.comroid.util.Debug; import org.comroid.util.MultithreadUtil; @@ -40,18 +42,10 @@ @ToString @Component.Requires(UpdateModule.class) @FieldDefaults(level = AccessLevel.PRIVATE) -public final class LocalExecutionModule extends ConsoleModule { +public final class LocalExecutionModule extends ConsoleModule { public static final Pattern DonePattern = pattern("Done \\((?