Files
fabric-carpet/src/main/java/carpet/CarpetServer.java
T
altrisi 5e8b4ece5b Migrate nullability annotations to jSpecify
Mojang has done that, we don't need 3 different sets of annotations
2025-12-01 13:46:33 +01:00

231 lines
9.2 KiB
Java

package carpet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import carpet.commands.CounterCommand;
import carpet.commands.DistanceCommand;
import carpet.commands.DrawCommand;
import carpet.commands.InfoCommand;
import carpet.commands.LogCommand;
import carpet.commands.MobAICommand;
import carpet.commands.PerimeterInfoCommand;
import carpet.commands.PlayerCommand;
import carpet.commands.ProfileCommand;
import carpet.script.ScriptCommand;
import carpet.commands.SpawnCommand;
import carpet.commands.TestCommand;
import carpet.network.ServerNetworkHandler;
import carpet.helpers.HopperCounter;
import carpet.logging.LoggerRegistry;
import carpet.script.CarpetScriptServer;
import carpet.api.settings.SettingsManager;
import carpet.logging.HUDController;
import carpet.script.external.Carpet;
import carpet.script.external.Vanilla;
import carpet.script.utils.ParticleParser;
import carpet.utils.MobAI;
import carpet.utils.SpawnReporter;
import com.mojang.brigadier.CommandDispatcher;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.commands.PerfCommand;
import net.minecraft.server.level.ServerPlayer;
import org.jspecify.annotations.Nullable;
public class CarpetServer // static for now - easier to handle all around the code, its one anyways
{
public static MinecraftServer minecraft_server;
public static CarpetScriptServer scriptServer;
public static carpet.settings.SettingsManager settingsManager; // to change type to api type, can't change right now because of binary and source compat
public static final List<CarpetExtension> extensions = new ArrayList<>();
/**
* Registers a {@link CarpetExtension} to be managed by Carpet.<br>
* Should be called before Carpet's startup, like in Fabric Loader's
* {@link net.fabricmc.api.ModInitializer} entrypoint
* @param extension The instance of a {@link CarpetExtension} to be registered
*/
public static void manageExtension(CarpetExtension extension)
{
extensions.add(extension);
// Stop the stupid practice of extensions mixing into Carpet just to register themselves
if (StackWalker.getInstance().walk(stream -> stream.skip(1)
.anyMatch(el -> el.getClassName() == CarpetServer.class.getName())))
{
CarpetSettings.LOG.warn("""
Extension '%s' is registering itself using a mixin into Carpet instead of a regular ModInitializer!
This is stupid and will crash the game in future versions!""".formatted(extension.getClass().getSimpleName()));
}
}
// Separate from onServerLoaded, because a server can be loaded multiple times in singleplayer
// Gets called by Fabric Loader from a ServerModInitializer and a ClientModInitializer, in both to allow extensions
// to register before this call in a ModInitializer (declared in fabric.mod.json)
public static void onGameStarted()
{
settingsManager = new carpet.settings.SettingsManager(CarpetSettings.carpetVersion, "carpet", "Carpet Mod");
settingsManager.parseSettingsClass(CarpetSettings.class);
extensions.forEach(CarpetExtension::onGameStarted);
//FabricAPIHooks.initialize();
CarpetScriptServer.parseFunctionClasses();
}
public static void onServerLoaded(MinecraftServer server)
{
CarpetServer.minecraft_server = server;
// shoudl not be needed - that bit needs refactoring, but not now.
SpawnReporter.resetSpawnStats(server, true);
forEachManager(sm -> sm.attachServer(server));
extensions.forEach(e -> e.onServerLoaded(server));
scriptServer = new CarpetScriptServer(server);
Carpet.MinecraftServer_addScriptServer(server, scriptServer);
MobAI.resetTrackers();
LoggerRegistry.initLoggers();
//TickSpeed.reset();
}
public static void onServerLoadedWorlds(MinecraftServer minecraftServer)
{
HopperCounter.resetAll(minecraftServer, true);
extensions.forEach(e -> e.onServerLoadedWorlds(minecraftServer));
// initialize scarpet rules after all extensions are loaded
forEachManager(SettingsManager::initializeScarpetRules);
scriptServer.initializeForWorld();
}
public static void tick(MinecraftServer server)
{
HUDController.update_hud(server, null);
if (scriptServer != null) scriptServer.tick();
//in case something happens
CarpetSettings.impendingFillSkipUpdates.set(false);
extensions.forEach(e -> e.onTick(server));
}
public static void registerCarpetCommands(CommandDispatcher<CommandSourceStack> dispatcher, Commands.CommandSelection environment, CommandBuildContext commandBuildContext)
{
if (settingsManager == null) // bootstrap dev initialization check
{
return;
}
forEachManager(sm -> sm.registerCommand(dispatcher, commandBuildContext));
ProfileCommand.register(dispatcher, commandBuildContext);
CounterCommand.register(dispatcher, commandBuildContext);
LogCommand.register(dispatcher, commandBuildContext);
SpawnCommand.register(dispatcher, commandBuildContext);
PlayerCommand.register(dispatcher, commandBuildContext);
InfoCommand.register(dispatcher, commandBuildContext);
DistanceCommand.register(dispatcher, commandBuildContext);
PerimeterInfoCommand.register(dispatcher, commandBuildContext);
DrawCommand.register(dispatcher, commandBuildContext);
ScriptCommand.register(dispatcher, commandBuildContext);
MobAICommand.register(dispatcher, commandBuildContext);
// registering command of extensions that has registered before either server is created
// for all other, they will have them registered when they add themselves
extensions.forEach(e -> {
e.registerCommands(dispatcher, commandBuildContext);
});
if (environment != Commands.CommandSelection.DEDICATED)
PerfCommand.register(dispatcher);
if (FabricLoader.getInstance().isDevelopmentEnvironment())
TestCommand.register(dispatcher);
// todo 1.16 - re-registerer apps if that's a reload operation.
}
public static void onPlayerLoggedIn(ServerPlayer player)
{
ServerNetworkHandler.onPlayerJoin(player);
LoggerRegistry.playerConnected(player);
extensions.forEach(e -> e.onPlayerLoggedIn(player));
scriptServer.onPlayerJoin(player);
}
public static void onPlayerLoggedOut(ServerPlayer player, Component reason)
{
ServerNetworkHandler.onPlayerLoggedOut(player);
LoggerRegistry.playerDisconnected(player);
extensions.forEach(e -> e.onPlayerLoggedOut(player));
// first case client, second case server
CarpetScriptServer runningScriptServer = (player.level().getServer() == null) ? scriptServer : Vanilla.MinecraftServer_getScriptServer(player.level().getServer());
if (runningScriptServer != null && !runningScriptServer.stopAll) {
runningScriptServer.onPlayerLoggedOut(player, reason);
}
}
public static void clientPreClosing()
{
if (scriptServer != null) scriptServer.onClose();
scriptServer = null;
}
public static void onServerClosed(@Nullable MinecraftServer server)
{
// this for whatever reason gets called multiple times even when joining on SP
// so we allow to pass multiple times gating it only on existing server ref
if (minecraft_server != null)
{
if (scriptServer != null) scriptServer.onClose();
// this is a mess, will cleanip onlly when global reference is gone
if (server != null && !Vanilla.MinecraftServer_getScriptServer(server).stopAll) {
Vanilla.MinecraftServer_getScriptServer(server).onClose();
}
scriptServer = null;
ServerNetworkHandler.close();
LoggerRegistry.stopLoggers();
HUDController.resetScarpetHUDs();
ParticleParser.resetCache();
extensions.forEach(e -> e.onServerClosed(server));
minecraft_server = null;
}
}
public static void onServerDoneClosing(MinecraftServer server)
{
forEachManager(SettingsManager::detachServer);
}
// not API
// carpet's included
public static void forEachManager(Consumer<SettingsManager> consumer)
{
consumer.accept(settingsManager);
for (CarpetExtension e : extensions)
{
SettingsManager manager = e.extensionSettingsManager();
if (manager != null)
{
consumer.accept(manager);
}
}
}
public static void registerExtensionLoggers()
{
extensions.forEach(CarpetExtension::registerLoggers);
}
public static void onReload(MinecraftServer server)
{
scriptServer.reload(server);
extensions.forEach(e -> e.onReload(server));
}
}