Skip to content

Commit

Permalink
chore: clean up AllayServer.java
Browse files Browse the repository at this point in the history
  • Loading branch information
smartcmd committed Nov 13, 2024
1 parent 77c2b5e commit a974ac0
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 73 deletions.
3 changes: 3 additions & 0 deletions api/src/main/java/org/allaymc/api/server/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ static Server getInstance() {
*/
long getStartTime();

/**
* Disconnect all players with the default reason.
*/
default void disconnectAllPlayers() {
disconnectAllPlayers(TrKeys.M_DISCONNECT_CLOSED);
}
Expand Down
173 changes: 100 additions & 73 deletions server/src/main/java/org/allaymc/server/AllayServer.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.allaymc.server;

import eu.okaeri.configs.ConfigManager;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import lombok.SneakyThrows;
Expand Down Expand Up @@ -58,64 +57,91 @@
import org.cloudburstmc.protocol.bedrock.data.command.CommandOriginType;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnmodifiableView;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

/**
* @author daoge_cmd
*/
@Slf4j
public final class AllayServer implements Server {

private static final CommandOriginData SERVER_COMMAND_ORIGIN_DATA = new CommandOriginData(CommandOriginType.DEDICATED_SERVER, UUID.randomUUID(), "", 0);
public static final String BAN_INFO_FILE_NAME = "ban-info.yml";
public static final String WHITELIST_FILE_NAME = "whitelist.yml";
private static final AllayServer INSTANCE = new AllayServer();;
private static final String BAN_INFO_FILE_NAME = "ban-info.yml";
private static final String WHITELIST_FILE_NAME = "whitelist.yml";
private static final CommandOriginData SERVER_COMMAND_ORIGIN_DATA = new CommandOriginData(CommandOriginType.DEDICATED_SERVER, UUID.randomUUID(), "", 0);

private final boolean debug = Server.SETTINGS.genericSettings().debug();
private final Map<UUID, EntityPlayer> players = new ConcurrentHashMap<>();
private final AtomicBoolean isRunning;
private final AtomicBoolean isStarting;
private final AtomicBoolean isFullyStopped;
private final Map<UUID, EntityPlayer> players;
private final Map<UUID, PlayerListPacket.Entry> playerListEntryMap;
@Getter
private final AllayWorldPool worldPool = new AllayWorldPool();
private final AtomicBoolean isRunning = new AtomicBoolean(true);
private final AtomicBoolean isStarting = new AtomicBoolean(true);
private boolean isFullyStopped = false;
private final Object2ObjectMap<UUID, PlayerListPacket.Entry> playerListEntryMap = new Object2ObjectOpenHashMap<>();
private final AllayWorldPool worldPool;
@Getter
private final PlayerStorage playerStorage = Server.SETTINGS.storageSettings().savePlayerData() ? new AllayNBTFilePlayerStorage(Path.of("players")) : AllayEmptyPlayerStorage.INSTANCE;
// Thread pool for executing CPU-intensive tasks
private final PlayerStorage playerStorage;
@Getter
private final ThreadPoolExecutor computeThreadPool = new ThreadPoolExecutor(
Server.SETTINGS.genericSettings().maxComputeThreadCount() <= 0 ? Runtime.getRuntime().availableProcessors() : Server.SETTINGS.genericSettings().maxComputeThreadCount(),
// maximumPoolSize and keepAliveTime are both meaningless, because we are using LinkedBlockingQueue
Server.SETTINGS.genericSettings().maxComputeThreadCount() <= 0 ? Runtime.getRuntime().availableProcessors() : Server.SETTINGS.genericSettings().maxComputeThreadCount(), 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(), AllayComputeThread::new);
// Thread pool for executing I/O-intensive tasks
private final ThreadPoolExecutor computeThreadPool;
@Getter
private final ExecutorService virtualThreadPool = Executors.newVirtualThreadPerTaskExecutor();
private final ExecutorService virtualThreadPool;
@Getter
private final EventBus eventBus = new AllayEventBus(Executors.newVirtualThreadPerTaskExecutor());
private final BanInfo banInfo = ConfigManager.create(BanInfo.class, Utils.createConfigInitializer(Path.of(BAN_INFO_FILE_NAME)));
private final Whitelist whitelist = ConfigManager.create(Whitelist.class, Utils.createConfigInitializer(Path.of(WHITELIST_FILE_NAME)));

private final EventBus eventBus;
@Getter
private final ScoreboardService scoreboardService;
private final BanInfo banInfo;
private final Whitelist whitelist;
@Getter
private final PluginManager pluginManager = new AllayPluginManager();
private final PluginManager pluginManager;
@Getter
private final Scheduler scheduler = new AllayScheduler(virtualThreadPool);
private final Scheduler scheduler;
@Getter
private final NetworkServer networkServer = new AllayNetworkServer(this);
private final AllayTerminalConsole terminalConsole = new AllayTerminalConsole(AllayServer.this);
private final NetworkServer networkServer;
private final AllayTerminalConsole terminalConsole;
private final GameLoop gameLoop;

@Getter
private ScoreboardService scoreboardService;
private final GameLoop gameLoop = GameLoop.builder().loopCountPerSec(20).onTick(gameLoop -> {
private long startTime;

public static AllayServer getInstance() {
return INSTANCE;
}

private AllayServer() {
this.isRunning = new AtomicBoolean(true);
this.isStarting = new AtomicBoolean(true);
this.isFullyStopped = new AtomicBoolean(false);
this.players = new ConcurrentHashMap<>();
this.playerListEntryMap = new Object2ObjectOpenHashMap<>();
this.worldPool = new AllayWorldPool();
this.playerStorage = Server.SETTINGS.storageSettings().savePlayerData() ? new AllayNBTFilePlayerStorage(Path.of("players")) : AllayEmptyPlayerStorage.INSTANCE;
this.computeThreadPool = createComputeThreadPool();
this.virtualThreadPool = Executors.newVirtualThreadPerTaskExecutor();
this.eventBus = new AllayEventBus(Executors.newVirtualThreadPerTaskExecutor());
this.scoreboardService = new ScoreboardService(this, new JsonScoreboardStorage(Path.of("command_data/scoreboards.json")));
this.banInfo = ConfigManager.create(BanInfo.class, Utils.createConfigInitializer(Path.of(BAN_INFO_FILE_NAME)));
this.whitelist = ConfigManager.create(Whitelist.class, Utils.createConfigInitializer(Path.of(WHITELIST_FILE_NAME)));
this.pluginManager = new AllayPluginManager();
this.scheduler = new AllayScheduler(virtualThreadPool);
this.networkServer = new AllayNetworkServer(this);
this.terminalConsole = new AllayTerminalConsole(AllayServer.this);
this.gameLoop = GameLoop.builder()
.loopCountPerSec(20)
.onStart(this::onServerStart)
.onTick(this::serverThreadMain)
.onStop(this::onServerStop)
.build();
}

private void serverThreadMain(GameLoop gameLoop) {
if (!isRunning.get()) {
gameLoop.stop();
return;
Expand All @@ -126,34 +152,45 @@ public final class AllayServer implements Server {
} catch (Throwable throwable) {
log.error("Error while ticking the server", throwable);
}
}).onStart(() -> isStarting.set(false)).onStop(() -> {
}

private void onServerStart() {
this.isStarting.set(false);
}

private void onServerStop() {
try {
shutdownReally();
} catch (Throwable throwable) {
log.error("Error while stopping the server", throwable);
}
}).build();
@Getter
private long startTime;
}

public static AllayServer getInstance() {
return INSTANCE;
private ThreadPoolExecutor createComputeThreadPool() {
return new ThreadPoolExecutor(
Server.SETTINGS.genericSettings().maxComputeThreadCount() <= 0 ? Runtime.getRuntime().availableProcessors() : Server.SETTINGS.genericSettings().maxComputeThreadCount(),
// maximumPoolSize and keepAliveTime are both meaningless, because we are using LinkedBlockingQueue
Server.SETTINGS.genericSettings().maxComputeThreadCount() <= 0 ? Runtime.getRuntime().availableProcessors() : Server.SETTINGS.genericSettings().maxComputeThreadCount(), 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(), AllayComputeThread::new);
}

@SneakyThrows
public void start(long initialTime) {
var ctx = (LoggerContext) LogManager.getContext(false);
var log4jConfig = ctx.getConfiguration();
var loggerConfig = log4jConfig.getLoggerConfig(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
if (debug && Level.TRACE.isLessSpecificThan(loggerConfig.getLevel())) {
if (Server.SETTINGS.genericSettings().debug() && Level.TRACE.isLessSpecificThan(loggerConfig.getLevel())) {
loggerConfig.setLevel(Level.TRACE);
ctx.updateLoggers();
}

SignalUtils.addTask(() -> {
if (!isRunning.get()) return;
if (!isRunning.get()) {
return;
}

shutdown();
while (!isFullyStopped) {
while (!isFullyStopped.get()) {
Thread.yield();
}
});
Expand All @@ -163,22 +200,13 @@ public void start(long initialTime) {
}

((AllayPluginManager) pluginManager).loadPlugins();

worldPool.loadWorlds();

var cmdDataPath = Path.of("command_data");
if (!Files.exists(cmdDataPath)) Files.createDirectory(cmdDataPath);
scoreboardService = new ScoreboardService(
this,
new JsonScoreboardStorage(Path.of("command_data/scoreboards.json"))
);
scoreboardService.read();

this.worldPool.loadWorlds();
this.scoreboardService.read();
((AllayPluginManager) pluginManager).enablePlugins();

sendTr(TrKeys.A_NETWORK_SERVER_STARTING);
networkServer.start();
startTime = System.currentTimeMillis();
this.networkServer.start();
this.startTime = System.currentTimeMillis();
sendTr(
TrKeys.A_NETWORK_SERVER_STARTED,
SETTINGS.networkSettings().ip(),
Expand All @@ -187,15 +215,15 @@ public void start(long initialTime) {
);

Metrics.AllayMetrics.startMetrics();

if (SETTINGS.genericSettings().enableGui()) Allay.DASHBOARD.serverStarted();

gameLoop.startLoop();
if (SETTINGS.genericSettings().enableGui()) {
Allay.DASHBOARD.serverStarted();
}
this.gameLoop.startLoop();
}

private void tick(long currentTick) {
this.scheduler.tick();
playerStorage.tick(currentTick);
this.playerStorage.tick(currentTick);
}

@Override
Expand All @@ -216,30 +244,29 @@ public void shutdown() {

private void shutdownReally() {
// Shutdown network server to prevent new client connecting to the server
networkServer.shutdown();
scheduler.shutdown();
this.networkServer.shutdown();
this.scheduler.shutdown();

var event = new ServerStopEvent();
event.call();
new ServerStopEvent().call();

// Disable all plugins firstly
((AllayPluginManager) pluginManager).disablePlugins();
((AllayPluginManager) this.pluginManager).disablePlugins();

// Save all configurations & data
SETTINGS.save();
banInfo.save();
whitelist.save();
scoreboardService.save();
playerStorage.shutdown();
Server.SETTINGS.save();
this.banInfo.save();
this.whitelist.save();
this.scoreboardService.save();
this.playerStorage.shutdown();

// Shutdown all worlds
worldPool.shutdown();
this.worldPool.shutdown();

// Shutdown thread pools
virtualThreadPool.shutdown();
computeThreadPool.shutdown();
this.virtualThreadPool.shutdown();
this.computeThreadPool.shutdown();

isFullyStopped = true;
this.isFullyStopped.set(true);
}

@Override
Expand Down Expand Up @@ -505,7 +532,7 @@ public CommandOriginData getCommandOriginData() {

@Override
public Location3fc getCmdExecuteLocation() {
return new Location3f(0, 0, 0, getWorldPool().getDefaultWorld().getDimension(DimensionInfo.OVERWORLD.dimensionId()));
return new Location3f(0, 0, 0, getWorldPool().getDefaultWorld().getOverWorld());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public class JsonScoreboardStorage implements ScoreboardStorage {
public JsonScoreboardStorage(Path path) {
this.filePath = path;
try {
if (!Files.exists(this.filePath.getParent())) {
Files.createDirectories(this.filePath.getParent());
}
if (!Files.exists(this.filePath)) {
Files.createFile(this.filePath);
}
Expand Down

0 comments on commit a974ac0

Please sign in to comment.