220 lines
10 KiB
Java
220 lines
10 KiB
Java
/*
|
|
* Minecraft Forge
|
|
* Copyright (c) 2016-2020.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation version 2.1
|
|
* of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
package net.minecraftforge.fml.server;
|
|
|
|
import static net.minecraftforge.fml.Logging.CORE;
|
|
|
|
import java.nio.file.Path;
|
|
import java.util.Map;
|
|
import java.util.Map.Entry;
|
|
import java.util.Objects;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.function.BiConsumer;
|
|
import java.util.function.Consumer;
|
|
|
|
import net.minecraft.resources.IPackNameDecorator;
|
|
import net.minecraft.world.storage.FolderName;
|
|
import net.minecraftforge.fml.DistExecutor;
|
|
import net.minecraftforge.fml.LogicalSidedProvider;
|
|
import net.minecraftforge.fml.ModLoader;
|
|
import net.minecraftforge.fml.ModLoadingStage;
|
|
import net.minecraftforge.fml.ModLoadingWarning;
|
|
import net.minecraftforge.fml.network.ConnectionType;
|
|
import net.minecraftforge.fml.network.FMLNetworkConstants;
|
|
import net.minecraftforge.fml.network.FMLStatusPing;
|
|
import net.minecraftforge.fml.network.NetworkHooks;
|
|
import net.minecraftforge.fml.network.NetworkRegistry;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
import org.apache.logging.log4j.Marker;
|
|
import org.apache.logging.log4j.MarkerManager;
|
|
|
|
import net.minecraft.network.NetworkManager;
|
|
import net.minecraft.network.ProtocolType;
|
|
import net.minecraft.network.handshake.client.CHandshakePacket;
|
|
import net.minecraft.network.login.server.SDisconnectLoginPacket;
|
|
import net.minecraft.resources.ResourcePackInfo;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.util.text.StringTextComponent;
|
|
import net.minecraftforge.api.distmarker.Dist;
|
|
import net.minecraftforge.common.MinecraftForge;
|
|
import net.minecraftforge.fml.config.ConfigTracker;
|
|
import net.minecraftforge.fml.config.ModConfig;
|
|
import net.minecraftforge.fml.event.server.FMLServerAboutToStartEvent;
|
|
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
|
|
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
|
|
import net.minecraftforge.fml.event.server.FMLServerStoppedEvent;
|
|
import net.minecraftforge.fml.event.server.FMLServerStoppingEvent;
|
|
import net.minecraftforge.fml.loading.FileUtils;
|
|
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
|
import net.minecraftforge.fml.packs.ModFileResourcePack;
|
|
import net.minecraftforge.fml.packs.ResourcePackLoader;
|
|
import net.minecraftforge.forgespi.language.IModInfo;
|
|
import net.minecraftforge.network.VanillaConnectionNetworkFilter;
|
|
import net.minecraftforge.registries.GameData;
|
|
|
|
public class ServerLifecycleHooks
|
|
{
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
|
private static final Marker SERVERHOOKS = MarkerManager.getMarker("SERVERHOOKS");
|
|
private static final FolderName SERVERCONFIG = new FolderName("serverconfig");
|
|
private static volatile CountDownLatch exitLatch = null;
|
|
private static MinecraftServer currentServer;
|
|
|
|
private static Path getServerConfigPath(final MinecraftServer server)
|
|
{
|
|
final Path serverConfig = server.func_240776_a_(SERVERCONFIG);
|
|
FileUtils.getOrCreateDirectory(serverConfig, "serverconfig");
|
|
return serverConfig;
|
|
}
|
|
|
|
public static boolean handleServerAboutToStart(final MinecraftServer server)
|
|
{
|
|
currentServer = server;
|
|
currentServer.getServerStatusResponse().setForgeData(new FMLStatusPing()); //gathers NetworkRegistry data
|
|
// on the dedi server we need to force the stuff to setup properly
|
|
LogicalSidedProvider.setServer(()->server);
|
|
ConfigTracker.INSTANCE.loadConfigs(ModConfig.Type.SERVER, getServerConfigPath(server));
|
|
return !MinecraftForge.EVENT_BUS.post(new FMLServerAboutToStartEvent(server));
|
|
}
|
|
|
|
public static boolean handleServerStarting(final MinecraftServer server)
|
|
{
|
|
DistExecutor.runWhenOn(Dist.DEDICATED_SERVER, ()->()->LanguageHook.loadLanguagesOnServer(server));
|
|
return !MinecraftForge.EVENT_BUS.post(new FMLServerStartingEvent(server));
|
|
}
|
|
|
|
public static void handleServerStarted(final MinecraftServer server)
|
|
{
|
|
MinecraftForge.EVENT_BUS.post(new FMLServerStartedEvent(server));
|
|
allowLogins.set(true);
|
|
}
|
|
|
|
public static void handleServerStopping(final MinecraftServer server)
|
|
{
|
|
allowLogins.set(false);
|
|
MinecraftForge.EVENT_BUS.post(new FMLServerStoppingEvent(server));
|
|
}
|
|
|
|
public static void expectServerStopped()
|
|
{
|
|
exitLatch = new CountDownLatch(1);
|
|
}
|
|
|
|
public static void handleServerStopped(final MinecraftServer server)
|
|
{
|
|
if (!server.isDedicatedServer()) GameData.revertToFrozen();
|
|
MinecraftForge.EVENT_BUS.post(new FMLServerStoppedEvent(server));
|
|
currentServer = null;
|
|
LogicalSidedProvider.setServer(null);
|
|
CountDownLatch latch = exitLatch;
|
|
|
|
if (latch != null)
|
|
{
|
|
latch.countDown();
|
|
exitLatch = null;
|
|
}
|
|
ConfigTracker.INSTANCE.unloadConfigs(ModConfig.Type.SERVER, getServerConfigPath(server));
|
|
}
|
|
|
|
public static MinecraftServer getCurrentServer()
|
|
{
|
|
return currentServer;
|
|
}
|
|
private static AtomicBoolean allowLogins = new AtomicBoolean(false);
|
|
|
|
public static boolean handleServerLogin(final CHandshakePacket packet, final NetworkManager manager) {
|
|
if (!allowLogins.get())
|
|
{
|
|
StringTextComponent text = new StringTextComponent("Server is still starting! Please wait before reconnecting.");
|
|
LOGGER.info(SERVERHOOKS,"Disconnecting Player (server is still starting): {}", text.getUnformattedComponentText());
|
|
manager.sendPacket(new SDisconnectLoginPacket(text));
|
|
manager.closeChannel(text);
|
|
return false;
|
|
}
|
|
|
|
if (packet.getRequestedState() == ProtocolType.LOGIN) {
|
|
final ConnectionType connectionType = ConnectionType.forVersionFlag(packet.getFMLVersion());
|
|
final int versionNumber = connectionType.getFMLVersionNumber(packet.getFMLVersion());
|
|
|
|
// HACK: Check for missing leading 0. If it's not there, we know the client isn't patched.
|
|
if (connectionType == ConnectionType.MODDED && packet.getFMLVersion().substring(FMLNetworkConstants.FMLNETMARKER.length()).charAt(0) != '0') {
|
|
rejectConnection(manager, connectionType, "Your client appears to be vulnerable to CVE-2021-44228, 45046, 45105, and/or 44832. To connect to this server, you must install ~keith's patched Forge version: https://bytes.keithhacks.cyou/keith/ForgePatch/releases/");
|
|
return false;
|
|
}
|
|
|
|
if (connectionType == ConnectionType.MODDED && versionNumber != FMLNetworkConstants.FMLNETVERSION) {
|
|
rejectConnection(manager, connectionType, "This modded server is not network compatible with your modded client. Please verify your Forge version closely matches the server. Got net version "+ versionNumber + " this server is net version "+FMLNetworkConstants.FMLNETVERSION);
|
|
return false;
|
|
}
|
|
|
|
if (connectionType == ConnectionType.VANILLA && !NetworkRegistry.acceptsVanillaClientConnections()) {
|
|
rejectConnection(manager, connectionType, "This server has mods that require Forge to be installed on the client. Contact your server admin for more details.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (packet.getRequestedState() == ProtocolType.STATUS) return true;
|
|
|
|
NetworkHooks.registerServerLoginChannel(manager, packet);
|
|
VanillaConnectionNetworkFilter.injectIfNecessary(manager);
|
|
return true;
|
|
|
|
}
|
|
|
|
private static void rejectConnection(final NetworkManager manager, ConnectionType type, String message) {
|
|
manager.setConnectionState(ProtocolType.LOGIN);
|
|
LOGGER.info(SERVERHOOKS, "Disconnecting {} connection attempt: {}", type, message);
|
|
StringTextComponent text = new StringTextComponent(message);
|
|
manager.sendPacket(new SDisconnectLoginPacket(text));
|
|
manager.closeChannel(text);
|
|
}
|
|
|
|
public static void handleExit(int retVal)
|
|
{
|
|
System.exit(retVal);
|
|
}
|
|
|
|
//INTERNAL MODDERS DO NOT USE
|
|
@Deprecated
|
|
public static ResourcePackLoader.IPackInfoFinder buildPackFinder(Map<ModFile, ? extends ModFileResourcePack> modResourcePacks, BiConsumer<? super ModFileResourcePack, ResourcePackInfo> packSetter) {
|
|
return (packList, factory) -> serverPackFinder(modResourcePacks, packSetter, packList, factory);
|
|
}
|
|
|
|
private static void serverPackFinder(Map<ModFile, ? extends ModFileResourcePack> modResourcePacks, BiConsumer<? super ModFileResourcePack, ResourcePackInfo> packSetter, Consumer<ResourcePackInfo> consumer, ResourcePackInfo.IFactory factory) {
|
|
for (Entry<ModFile, ? extends ModFileResourcePack> e : modResourcePacks.entrySet())
|
|
{
|
|
IModInfo mod = e.getKey().getModInfos().get(0);
|
|
if (Objects.equals(mod.getModId(), "minecraft")) continue; // skip the minecraft "mod"
|
|
final String name = "mod:" + mod.getModId();
|
|
final ResourcePackInfo packInfo = ResourcePackInfo.createResourcePack(name, false, e::getValue, factory, ResourcePackInfo.Priority.BOTTOM, IPackNameDecorator.PLAIN);
|
|
if (packInfo == null) {
|
|
// Vanilla only logs an error, instead of propagating, so handle null and warn that something went wrong
|
|
ModLoader.get().addWarning(new ModLoadingWarning(mod, ModLoadingStage.ERROR, "fml.modloading.brokenresources", e.getKey()));
|
|
continue;
|
|
}
|
|
packSetter.accept(e.getValue(), packInfo);
|
|
LOGGER.debug(CORE, "Generating PackInfo named {} for mod file {}", name, e.getKey().getFilePath());
|
|
consumer.accept(packInfo);
|
|
}
|
|
}
|
|
}
|