org.spongepowered.lantern.LanternServer.java Source code

Java tutorial

Introduction

Here is the source code for org.spongepowered.lantern.LanternServer.java

Source

/*
 * This file is part of Sponge, licensed under the MIT License (MIT).
 *
 * Copyright (c) SpongePowered <https://www.spongepowered.org>
 * Copyright (c) contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.spongepowered.lantern;

import com.google.common.util.concurrent.ListenableFuture;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import org.spongepowered.api.Server;
import org.spongepowered.api.command.source.ConsoleSource;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.SpongeEventFactory;
import org.spongepowered.api.event.cause.Cause;
import org.spongepowered.api.network.status.Favicon;
import org.spongepowered.api.profile.GameProfileManager;
import org.spongepowered.api.resourcepack.ResourcePack;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.text.Texts;
import org.spongepowered.api.text.sink.MessageSink;
import org.spongepowered.api.world.ChunkTicketManager;
import org.spongepowered.api.world.DimensionTypes;
import org.spongepowered.api.world.GeneratorTypes;
import org.spongepowered.api.world.World;
import org.spongepowered.api.world.WorldCreationSettings;
import org.spongepowered.api.world.storage.ChunkLayout;
import org.spongepowered.api.world.storage.WorldProperties;
import org.spongepowered.lantern.config.LanternConfig;
import org.spongepowered.lantern.launch.console.ConsoleManager;
import org.spongepowered.lantern.network.LanternNetworkServer;
import org.spongepowered.lantern.network.LanternSession;
import org.spongepowered.lantern.network.SessionRegistry;
import org.spongepowered.lantern.registry.type.world.WorldPropertyRegistryModule;
import org.spongepowered.lantern.scheduler.LanternScheduler;
import org.spongepowered.lantern.world.LanternWorld;
import org.spongepowered.lantern.world.LanternWorldBuilder;
import org.spongepowered.lantern.world.storage.LanternChunkLayout;
import org.spongepowered.lantern.world.storage.LanternWorldProperties;
import org.spongepowered.lantern.world.storage.LanternWorldStorage;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.UUID;

public class LanternServer implements Server {

    public static final String NETHER_NAME = "DIM-1";
    public static final String THE_END_NAME = "DIM1";

    /**
     * A list of all active {@link LanternSession}s.
     */
    private final SessionRegistry sessionRegistry = new SessionRegistry();

    /**
     * The console manager of this server.
     */
    private final ConsoleManager consoleManager = new ConsoleManager();
    /**
     * The network server that manages the connection.
     */
    private final LanternNetworkServer networkServer = new LanternNetworkServer(this);

    public LanternServer() throws BindException {
        //TODO: Commandline Args?
        //TODO: Get Ops, whitelist, bans

        start();
        bind();
        //TODO: Query and rcon
    }

    public void start() {
        consoleManager.startConsole();
        consoleManager.startFile(SpongeImpl.getGlobalConfig().getConfig().getLogFile());

        //TODO: Fire AboutToStart

        //TODO: Fire AboutToStart

        //TODO: Load Ops, whitelist, bans

        //TODO: Load worlds
        loadAllWorlds();

        SpongeImpl.getGame().getScheduler().start();
    }

    public void loadAllWorlds() {
        Path worlds = SpongeImpl.getWorldDirectory();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(worlds)) {
            stream.forEach(world -> {
                Path spongeFile = world.resolve("level_sponge.dat");
                if (!Files.exists(spongeFile))
                    return;

                loadWorld(world.getFileName().toString());
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

        LanternConfig.GlobalConfig config = SpongeImpl.getGlobalConfig().getConfig();
        LanternConfig.ConfigBase worldConfig = SpongeImpl
                .getActiveConfig(DimensionTypes.OVERWORLD.getId(), config.getLevelName()).getConfig();
        LanternConfig.ConfigBase netherConfig = SpongeImpl
                .getActiveConfig(DimensionTypes.NETHER.getId(), NETHER_NAME).getConfig();
        LanternConfig.ConfigBase endConfig = SpongeImpl
                .getActiveConfig(DimensionTypes.THE_END.getId(), THE_END_NAME).getConfig();

        // Load the default worlds if they aren't loaded already
        createWorld(new LanternWorldBuilder().name(config.getLevelName()).enabled(true).loadsOnStartup(true)
                .keepsSpawnLoaded(true).seed(config.getLevelSeed()).gameMode(config.getGamemode())
                .generator(worldConfig.getWorld().getGenerator()).dimensionType(DimensionTypes.OVERWORLD)
                .hardcore(worldConfig.getWorld().isHardcore())
                //                .generatorSettings(worldConfig.getWorld().getGeneratorSettings()) //TODO: Generator Settings
                .buildSettings(), SpongeImpl.getWorldDirectory());
        loadWorld(config.getLevelName(), SpongeImpl.getWorldDirectory());

        //TODO: Check whether to load
        createWorld(new LanternWorldBuilder().name(NETHER_NAME).enabled(config.isAllowNether()).loadsOnStartup(true)
                .keepsSpawnLoaded(false).seed(config.getLevelSeed()).gameMode(config.getGamemode())
                .generator(GeneratorTypes.NETHER).dimensionType(DimensionTypes.NETHER)
                .hardcore(netherConfig.getWorld().isHardcore())
                //                .generatorSettings(netherConfig.getWorld().getGeneratorSettings()) //TODO: Generator Settings
                .buildSettings());
        loadWorld(NETHER_NAME);

        //TODO: Check whether to load
        createWorld(new LanternWorldBuilder().name(THE_END_NAME).enabled(config.isAllowEnd()).loadsOnStartup(true)
                .keepsSpawnLoaded(false).seed(config.getLevelSeed()).gameMode(config.getGamemode())
                .generator(GeneratorTypes.THE_END).dimensionType(DimensionTypes.THE_END)
                .hardcore(endConfig.getWorld().isHardcore())
                //                .generatorSettings(endConfig.getWorld().getGeneratorSettings()) //TODO: Generator Settings
                .buildSettings());
        loadWorld(THE_END_NAME);
    }

    public void bind() throws BindException {
        SocketAddress address = getBindAddress();

        SpongeImpl.getLogger().info("Binding to address: " + address + "...");
        ChannelFuture future = networkServer.bind(address);
        Channel channel = future.awaitUninterruptibly().channel();
        if (!channel.isActive()) {
            Throwable cause = future.cause();
            if (cause instanceof BindException) {
                throw (BindException) cause;
            }
            throw new RuntimeException("Failed to bind to address", cause);
        }

        SpongeImpl.getLogger().info("Successfully bound to: " + channel.localAddress());
    }

    private SocketAddress getBindAddress() {
        String ip = SpongeImpl.getGlobalConfig().getConfig().getServerIp();
        int port = SpongeImpl.getGlobalConfig().getConfig().getServerPort();

        if (ip.length() == 0) {
            return new InetSocketAddress(port);
        }

        return new InetSocketAddress(ip, port);
    }

    @Override
    public Collection<Player> getOnlinePlayers() {
        return Collections.emptySet(); //TODO: Implement
    }

    @Override
    public int getMaxPlayers() {
        return 0; //TODO: Implement
    }

    @Override
    public Optional<Player> getPlayer(UUID uniqueId) {
        return Optional.empty(); //TODO: Implement
    }

    @Override
    public Optional<Player> getPlayer(String name) {
        return Optional.empty(); //TODO: Implement
    }

    @Override
    public Collection<World> getWorlds() {
        return Collections.unmodifiableCollection(LanternScheduler.getInstance().getWorldScheduler().getWorlds());
    }

    @Override
    public Collection<WorldProperties> getUnloadedWorlds() {
        return Collections.emptySet(); //TODO: Implement
    }

    @Override
    public Collection<WorldProperties> getAllWorldProperties() {
        return Collections.emptySet(); //TODO: Implement
    }

    @Override
    public Optional<World> getWorld(UUID uniqueId) {
        for (World world : getWorlds()) {
            if (world.getUniqueId().equals(uniqueId))
                return Optional.of(world);
        }
        return Optional.empty();
    }

    @Override
    public Optional<World> getWorld(String worldName) {
        for (World world : getWorlds()) {
            if (world.getName().equals(worldName))
                return Optional.of(world);
        }
        return Optional.empty();
    }

    @Override
    public Optional<WorldProperties> getDefaultWorld() {
        return Optional.empty(); //TODO: Implement
    }

    @Override
    public Optional<World> loadWorld(UUID uniqueId) {
        return Optional.empty(); //TODO: Implement
    }

    @Override
    public Optional<World> loadWorld(WorldProperties properties) {
        if (properties == null)
            return Optional.empty();
        return loadWorld(properties.getWorldName());
    }

    @Override
    public Optional<World> loadWorld(String worldName) {
        final Optional<World> existing = getWorld(worldName);
        if (existing.isPresent())
            return existing;

        return loadWorld(worldName, SpongeImpl.getWorldDirectory().resolve(worldName));
    }

    public Optional<World> loadWorld(String worldName, Path worldFolder) {
        final Optional<World> existing = getWorld(worldName);
        if (existing.isPresent())
            return existing;

        if (Files.isRegularFile(worldFolder)) {
            throw new IllegalArgumentException("File exists with the name '" + worldName + "' and isn't a folder");
        }

        LanternWorldStorage storage = new LanternWorldStorage(worldFolder);
        Optional<WorldProperties> optProps = WorldPropertyRegistryModule.getInstance()
                .getWorldProperties(worldName);
        WorldProperties properties = optProps.orElse(storage.getWorldProperties());
        if (properties instanceof LanternWorldProperties
                && ((LanternWorldProperties) properties).getWorldConfig() == null) {
            ((LanternWorldProperties) properties).setWorldConfig(
                    SpongeImpl.getWorldConfig(worldName, properties.getDimensionType()).getConfig());
        }

        if (!properties.isEnabled()) {
            SpongeImpl.getLogger().error("Unable to load world " + worldName + ". World is disabled!");
            return Optional.empty();
        }

        int dim = ((LanternWorldProperties) properties).getDimensionId();
        //TODO: Register dimension ID?
        if (!WorldPropertyRegistryModule.getInstance().isWorldRegistered(worldName)) {
            WorldPropertyRegistryModule.getInstance().registerWorldProperties(properties);
        }

        LanternWorld world = new LanternWorld(storage, properties);
        LanternScheduler.getInstance().getWorldScheduler().addWorld(world);
        //TODO: Init spawn?
        Lantern.post(SpongeEventFactory.createLoadWorldEvent(SpongeImpl.getGame(), Cause.of(this), world));

        return Optional.of(world);
    }

    @Override
    public Optional<WorldProperties> getWorldProperties(String worldName) {
        Optional<WorldProperties> loaded = WorldPropertyRegistryModule.getInstance().getWorldProperties(worldName);
        if (!loaded.isPresent())
            return loaded;

        Path worlds = SpongeImpl.getWorldDirectory();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(worlds)) {
            for (Path world : stream) {
                if (!world.getFileName().toString().equals(worldName))
                    continue;

                Path spongeFile = world.resolve("level_sponge.dat");
                if (!Files.exists(spongeFile))
                    continue;
                return Optional.of(new LanternWorldStorage(world).getWorldProperties());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return Optional.empty();
    }

    @Override
    public Optional<WorldProperties> getWorldProperties(UUID uniqueId) {
        Optional<WorldProperties> loaded = WorldPropertyRegistryModule.getInstance().getWorldProperties(uniqueId);
        if (!loaded.isPresent())
            return loaded;

        Path worlds = SpongeImpl.getWorldDirectory();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(worlds)) {
            for (Path world : stream) {
                Path spongeFile = world.resolve("level_sponge.dat");
                if (!Files.exists(spongeFile))
                    continue;
                WorldProperties properties = new LanternWorldStorage(world).getWorldProperties();
                if (properties.getUniqueId().equals(uniqueId))
                    return Optional.of(properties);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return Optional.empty();
    }

    @Override
    public boolean unloadWorld(World world) {
        return false; //TODO: Implement
    }

    @Override
    public Optional<WorldProperties> createWorld(WorldCreationSettings settings) {
        String name = settings.getWorldName();
        Optional<World> existing = getWorld(name);
        if (existing.isPresent())
            return Optional.of(existing.get().getProperties());

        return createWorld(settings, SpongeImpl.getWorldDirectory().resolve(name));
    }

    public Optional<WorldProperties> createWorld(WorldCreationSettings settings, Path worldDir) {
        String name = settings.getWorldName();
        Optional<World> existing = getWorld(name);
        if (existing.isPresent())
            return Optional.of(existing.get().getProperties());

        LanternConfig.WorldConfig config = SpongeImpl
                .getWorldConfig(settings.getWorldName(), settings.getDimensionType()).getConfig();
        LanternWorldStorage storage = new LanternWorldStorage(worldDir);
        LanternWorldProperties properties = new LanternWorldProperties(settings);
        properties.setWorldConfig(config);
        // Ensure these are set on the config
        properties.setEnabled(settings.isEnabled());
        properties.setLoadOnStartup(settings.loadOnStartup());
        properties.setPVPEnabled(settings.isPVPEnabled());

        try {
            storage.writeWorldProperties(properties);
        } catch (IOException e) {
            SpongeImpl.getLogger().error("Unable to create world properties for " + settings.getWorldName(), e);
            return Optional.empty();
        }

        // TODO: ConstructWorldEvent?
        if (!WorldPropertyRegistryModule.getInstance().isWorldRegistered(properties.getUniqueId())) {
            WorldPropertyRegistryModule.getInstance().registerWorldProperties(properties);
            return Optional.of(properties);
        } else {
            return WorldPropertyRegistryModule.getInstance().getWorldProperties(properties.getUniqueId());
        }
    }

    @Override
    public ListenableFuture<Optional<WorldProperties>> copyWorld(WorldProperties worldProperties, String copyName) {
        return null; //TODO: Implement
    }

    @Override
    public Optional<WorldProperties> renameWorld(WorldProperties worldProperties, String newName) {
        return null; //TODO: Implement
    }

    @Override
    public ListenableFuture<Boolean> deleteWorld(WorldProperties worldProperties) {
        return null; //TODO: Implement
    }

    @Override
    public boolean saveWorldProperties(WorldProperties properties) {
        return false; //TODO: Implement
    }

    @Override
    public ChunkLayout getChunkLayout() {
        return LanternChunkLayout.instance;
    }

    @Override
    public int getRunningTimeTicks() {
        return 0; //TODO: Implement
    }

    @Override
    public MessageSink getBroadcastSink() {
        return null; //TODO: Implement
    }

    @Override
    public Optional<InetSocketAddress> getBoundAddress() {
        return null; //TODO: Implement
    }

    @Override
    public boolean hasWhitelist() {
        return false; //TODO: Implement
    }

    @Override
    public void setHasWhitelist(boolean enabled) {
        //TODO: Implement
    }

    @Override
    public boolean getOnlineMode() {
        return false; //TODO: Implement
    }

    @Override
    public Text getMotd() {
        return Texts.of(); //TODO: Implement
    }

    @Override
    public void shutdown() {
        //TODO: Implement
    }

    @Override
    public void shutdown(Text kickMessage) {
        //TODO: Implement
    }

    @Override
    public ConsoleSource getConsole() {
        return consoleManager.getSender();
    }

    @Override
    public double getTicksPerSecond() {
        return 0; //TODO: Implement
    }

    @Override
    public Optional<ResourcePack> getDefaultResourcePack() {
        return Optional.empty(); //TODO: Implement
    }

    @Override
    public ChunkTicketManager getChunkTicketManager() {
        return null; //TODO: Implement
    }

    @Override
    public GameProfileManager getGameProfileManager() {
        return null; //TODO: Implement
    }

    public SessionRegistry getSessionRegistry() {
        return sessionRegistry;
    }

    public boolean getProxySupport() {
        return SpongeImpl.getGlobalConfig().getConfig().getBungeeCord().getIpForwarding();
    }

    public Optional<Favicon> getFavicon() {
        return Optional.empty(); //TODO: Implement
    }
}