160 lines
4.7 KiB
Java
160 lines
4.7 KiB
Java
package de.ph87.mc.server;
|
|
|
|
import jakarta.annotation.PostConstruct;
|
|
import lombok.NonNull;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.context.ApplicationEventPublisher;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.function.Consumer;
|
|
|
|
@Slf4j
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class ServerService {
|
|
|
|
private final ApplicationEventPublisher applicationEventPublisher;
|
|
|
|
private final ServerConfig serverConfig;
|
|
|
|
private final Object serversLock = new Object();
|
|
|
|
private List<Server> servers = new ArrayList<>();
|
|
|
|
@PostConstruct
|
|
public void startup() {
|
|
final File ROOT = new File(serverConfig.getPath());
|
|
synchronized (serversLock) {
|
|
servers = Arrays.stream(Objects.requireNonNull(ROOT.listFiles())).map(this::_tryLoadingFromDir).filter(Optional::isPresent).map(Optional::get).toList();
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
private Optional<Server> _tryLoadingFromDir(@NonNull final File directory) {
|
|
try {
|
|
final Server server = new Server(directory);
|
|
servers.stream().filter(server::eq).findFirst().ifPresent(old -> server.process = old.process);
|
|
return Optional.of(server);
|
|
} catch (NoMinecraftServer e) {
|
|
log.warn(e.getMessage());
|
|
}
|
|
return Optional.empty();
|
|
}
|
|
|
|
@NonNull
|
|
public ServerDto start(@NonNull final String name) {
|
|
return set(name, this::start);
|
|
}
|
|
|
|
@NonNull
|
|
public ServerDto stop(@NonNull final String name) {
|
|
return set(name, this::stop);
|
|
}
|
|
|
|
private void start(@NonNull final Server server) {
|
|
synchronized (server.lock) {
|
|
if (server.isRunning()) {
|
|
log.warn("Server is already running: name={}", server.properties.name);
|
|
return;
|
|
}
|
|
stopAll();
|
|
log.info("Starting server: name={}", server.properties.name);
|
|
final ProcessBuilder builder = new ProcessBuilder("java", "-jar", "server.jar");
|
|
builder.directory(server.directory);
|
|
try {
|
|
server.process = builder.start();
|
|
log.info("Server started: name={}", server.properties.name);
|
|
} catch (IOException e) {
|
|
log.error("Failed to start Server: name={}, error={}", server.properties.name, e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
|
|
private void stop(@NonNull final Server server) {
|
|
synchronized (server.lock) {
|
|
if (!server.isRunning()) {
|
|
log.warn("Server is not running: name={}", server.properties.name);
|
|
return;
|
|
}
|
|
if (server.shutdown) {
|
|
log.warn("Server is already shutting down: name={}", server.properties.name);
|
|
return;
|
|
}
|
|
server.shutdown = true;
|
|
log.info("Stopping Server: name={}", server.properties.name);
|
|
new Thread(() -> _stop_async(server), "STOP-" + server.properties.name).start();
|
|
}
|
|
}
|
|
|
|
private void _stop_async(@NonNull final Server server) {
|
|
log.debug("Thread spawned: name={}", server.properties.name);
|
|
synchronized (server.lock) {
|
|
assert server.process != null;
|
|
log.info("Stopping server: name={}", server.properties.name);
|
|
server.process.destroy();
|
|
try {
|
|
server.process.waitFor();
|
|
log.info("Server stopped: name={}", server.properties.name);
|
|
} catch (InterruptedException e) {
|
|
log.error("Interrupted while waiting for server to stop: name={}", server.properties.name);
|
|
} finally {
|
|
server.process = null;
|
|
server.shutdown = false;
|
|
publish(server);
|
|
log.debug("Thread terminated: name={}", server.properties.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
private ServerDto set(final @NonNull String name, @NonNull final Consumer<Server> modifier) {
|
|
final Server server = getByName(name);
|
|
modifier.accept(server);
|
|
return publish(server);
|
|
}
|
|
|
|
@NonNull
|
|
private ServerDto publish(@NonNull final Server server) {
|
|
final ServerDto dto = new ServerDto(server);
|
|
applicationEventPublisher.publishEvent(dto);
|
|
return dto;
|
|
}
|
|
|
|
@NonNull
|
|
private Server getByName(@NonNull final String name) {
|
|
synchronized (serversLock) {
|
|
return servers.stream().filter(server -> server.properties.name.equals(name)).findFirst().orElseThrow();
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
public List<ServerDto> findAll() {
|
|
synchronized (serversLock) {
|
|
return servers.stream().map(ServerDto::new).toList();
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
public File getIconFileByName(@NonNull final String name) {
|
|
synchronized (serversLock) {
|
|
return getByName(name).iconFile;
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
public List<ServerDto> stopAll() {
|
|
synchronized (serversLock) {
|
|
return servers.stream().peek(this::stop).map(ServerDto::new).toList();
|
|
}
|
|
}
|
|
|
|
}
|