org.spout.engine.SpoutServer.java Source code

Java tutorial

Introduction

Here is the source code for org.spout.engine.SpoutServer.java

Source

/*
 * This file is part of Spout.
 *
 * Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
 * Spout is licensed under the SpoutDev License Version 1.
 *
 * Spout 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition, 180 days after any changes are published, you can use the
 * software, incorporating those changes, under the terms of the MIT license,
 * as described in the SpoutDev License Version 1.
 *
 * Spout 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,
 * the MIT license and the SpoutDev License Version 1 along with this program.
 * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
 * License and see <http://www.spout.org/SpoutDevLicenseV1.txt> for the full license,
 * including the MIT license.
 */
package org.spout.engine;

import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;

import org.apache.commons.lang3.Validate;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.teleal.cling.UpnpService;
import org.teleal.cling.UpnpServiceImpl;
import org.teleal.cling.controlpoint.ControlPoint;
import org.teleal.cling.support.igd.PortMappingListener;
import org.teleal.cling.support.model.PortMapping;
import org.teleal.cling.transport.spi.InitializationException;

import org.spout.api.FileSystem;
import org.spout.api.Server;
import org.spout.api.chat.ChatArguments;
import org.spout.api.chat.channel.ChatChannel;
import org.spout.api.command.CommandSource;
import org.spout.api.entity.Player;
import org.spout.api.event.Listener;
import org.spout.api.event.server.ServerStartEvent;
import org.spout.api.event.server.ServerStopEvent;
import org.spout.api.exception.ConfigurationException;
import org.spout.api.permissions.PermissionsSubject;
import org.spout.api.plugin.Platform;
import org.spout.api.protocol.CommonPipelineFactory;
import org.spout.api.protocol.PortBinding;
import org.spout.api.protocol.Protocol;
import org.spout.api.protocol.Session;
import org.spout.api.util.StringUtil;
import org.spout.api.util.access.AccessManager;

import org.spout.engine.entity.SpoutPlayer;
import org.spout.engine.filesystem.ServerFileSystem;
import org.spout.engine.listener.SpoutServerListener;
import org.spout.engine.protocol.PortBindingImpl;
import org.spout.engine.protocol.PortBindings;
import org.spout.engine.protocol.SpoutNioServerSocketChannel;
import org.spout.engine.protocol.SpoutServerSession;
import org.spout.engine.util.access.SpoutAccessManager;
import org.spout.engine.util.thread.threadfactory.NamedThreadFactory;
import org.spout.engine.world.SpoutWorld;

import static org.spout.api.lang.Translation.log;

public class SpoutServer extends SpoutEngine implements Server {
    /**
     * The name of the server
     */
    private final String name = "Spout Server";
    /**
     * The {@link FileSystem} for the server
     */
    private final FileSystem filesystem;
    /**
     * If the server allows flight.
     */
    private volatile boolean allowFlight = false;
    /**
     * The {@link ServerBootstrap} used to initialize Netty.
     */
    private final ServerBootstrap bootstrap = new ServerBootstrap();
    /**
     * The UPnP service
     */
    private UpnpService upnpService;
    /**
     * The {@link AccessManager} for the Server.
     */
    private final SpoutAccessManager accessManager = new SpoutAccessManager();

    public SpoutServer() {
        this.filesystem = new ServerFileSystem();
    }

    @Override
    public void start() {
        start(true);
    }

    @Override
    public void start(boolean checkWorlds) {
        start(checkWorlds, new SpoutServerListener(this));
    }

    public void start(boolean checkWorlds, Listener listener) {
        super.start(checkWorlds);
        getEventManager().registerEvents(listener, this);
        getFilesystem().postStartup();
        getEventManager().callEvent(new ServerStartEvent());
        log("Done Loading, ready for players.");
    }

    @Override
    protected void postPluginLoad(SpoutConfiguration config) {
        PortBindings portBindings = new PortBindings(this, config);
        try {
            portBindings.load(config);
            portBindings.bindAll();
            portBindings.save();
        } catch (ConfigurationException e) {
            log("Error loading port bindings: %0", Level.SEVERE, e);
        }

        if (boundProtocols.size() == 0) {
            log("No port bindings registered! Clients will not be able to connect to the server.", Level.WARNING);
        }
    }

    @Override
    public void init(SpoutApplication args) {
        super.init(args);
        //Note: All threads are daemons, cleanup of the executors is handled by bootstrap.getFactory().releaseExternalResources(); in stop(...).
        ExecutorService executorBoss = Executors
                .newCachedThreadPool(new NamedThreadFactory("SpoutServer - Boss", true));
        ExecutorService executorWorker = Executors
                .newCachedThreadPool(new NamedThreadFactory("SpoutServer - Worker", true));
        ChannelFactory factory = new SpoutNioServerSocketChannel(executorBoss, executorWorker);
        bootstrap.setFactory(factory);
        bootstrap.setOption("tcpNoDelay", true);
        bootstrap.setOption("keepAlive", true);

        ChannelPipelineFactory pipelineFactory = new CommonPipelineFactory(this, false);
        bootstrap.setPipelineFactory(pipelineFactory);

        accessManager.load();
        accessManager.setWhitelistEnabled(SpoutConfiguration.WHITELIST_ENABLED.getBoolean());
    }

    @Override
    public boolean stop(final String message) {
        if (!super.stop(message, false)) {
            return false;
        }
        Runnable lastTickTask = new Runnable() {
            @Override
            public void run() {
                ServerStopEvent stopEvent = new ServerStopEvent(message);
                getEventManager().callEvent(stopEvent);
                for (Player player : getOnlinePlayers()) {
                    player.kick(stopEvent.getMessage());
                }
                if (upnpService != null) {
                    upnpService.shutdown();
                }
            }
        };
        Runnable finalTask = new Runnable() {
            @Override
            public void run() {
                bootstrap.getFactory().releaseExternalResources();
                boundProtocols.clear();
            }
        };
        getScheduler().submitLastTickTask(lastTickTask);
        getScheduler().submitFinalTask(finalTask, false);
        getScheduler().stop();
        return true;
    }

    @Override
    public boolean bind(PortBinding binding) {
        Validate.notNull(binding);
        if (binding.getProtocol() == null) {
            throw new IllegalArgumentException("Protocol cannot be null");
        }

        if (boundProtocols.containsKey(binding.getAddress())) {
            return false;
        }
        boundProtocols.put(binding.getAddress(), binding.getProtocol());
        try {
            getChannelGroup().add(bootstrap.bind(binding.getAddress()));
        } catch (org.jboss.netty.channel.ChannelException ex) {
            log("Failed to bind to address %0. Is there already another server running on this address?",
                    Level.SEVERE, binding.getAddress(), ex);
            return false;
        }

        log("Binding to address: %0...", binding.getAddress());
        return true;
    }

    @Override
    public int getMaxPlayers() {
        return SpoutConfiguration.MAXIMUM_PLAYERS.getInt();
    }

    @Override
    public void save(boolean worlds, boolean players) {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean allowFlight() {
        return allowFlight;
    }

    @Override
    public List<PortBinding> getBoundAddresses() {
        List<PortBinding> bindings = new ArrayList<PortBinding>();
        for (Map.Entry<SocketAddress, Protocol> entry : boundProtocols.entrySet()) {
            bindings.add(new PortBindingImpl(entry.getValue(), entry.getKey()));
        }
        return Collections.unmodifiableList(bindings);
    }

    @Override
    public Session newSession(Channel channel) {
        Protocol protocol = getProtocol(channel.getLocalAddress());
        return new SpoutServerSession<SpoutServer>(this, channel, protocol);
    }

    @Override
    public Platform getPlatform() {
        return Platform.SERVER;
    }

    @Override
    public String getName() {
        return name;
    }

    private UpnpService getUPnPService() {
        if (upnpService == null) {
            try {
                upnpService = new UpnpServiceImpl();
            } catch (InitializationException e) {
                log("Could not enable UPnP Service: %0", Level.SEVERE, e.getMessage());
            }
        }

        return upnpService;
    }

    private PortMapping createPortMapping(int port, PortMapping.Protocol protocol, String description) {
        try {
            return new PortMapping(port, InetAddress.getLocalHost().getHostAddress(), protocol, description);
        } catch (UnknownHostException e) {
            Error error = new Error(
                    "Error while trying to retrieve the localhost while creating a PortMapping object.", e);
            getLogger().severe(e.getMessage());
            throw error;
        }
    }

    @Override
    public void mapUPnPPort(int port) {
        mapUPnPPort(port, null);
    }

    @Override
    public void mapUPnPPort(int port, String description) {
        UpnpService upnpService = getUPnPService();
        if (upnpService != null) {
            PortMapping[] desiredMapping = { createPortMapping(port, PortMapping.Protocol.TCP, description),
                    createPortMapping(port, PortMapping.Protocol.UDP, description) };
            PortMappingListener listener = new PortMappingListener(desiredMapping);

            ControlPoint controlPoint = upnpService.getControlPoint();
            controlPoint.getRegistry().addListener(listener);
            controlPoint.search();
        }
    }

    @Override
    public void mapTCPPort(int port) {
        mapTCPPort(port, null);
    }

    @Override
    public void mapTCPPort(int port, String description) {
        PortMapping desiredMapping = createPortMapping(port, PortMapping.Protocol.TCP, description);
        PortMappingListener listener = new PortMappingListener(desiredMapping);

        ControlPoint controlPoint = getUPnPService().getControlPoint();
        controlPoint.getRegistry().addListener(listener);
        controlPoint.search();
    }

    @Override
    public void mapUDPPort(int port) {
        mapUDPPort(port, null);
    }

    @Override
    public void mapUDPPort(int port, String description) {
        PortMapping desiredMapping = createPortMapping(port, PortMapping.Protocol.UDP, description);
        PortMappingListener listener = new PortMappingListener(desiredMapping);

        ControlPoint controlPoint = getUPnPService().getControlPoint();
        controlPoint.getRegistry().addListener(listener);
        controlPoint.search();
    }

    @Override
    public List<String> getAllPlayers() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public SpoutPlayer[] getOnlinePlayers() {
        Map<String, SpoutPlayer> playerList = players.get();
        ArrayList<SpoutPlayer> onlinePlayers = new ArrayList<SpoutPlayer>(playerList.size());
        for (SpoutPlayer player : playerList.values()) {
            if (player.isOnline()) {
                onlinePlayers.add(player);
            }
        }
        return onlinePlayers.toArray(new SpoutPlayer[onlinePlayers.size()]);
    }

    @Override
    public void broadcastMessage(Object... message) {
        broadcastMessage(STANDARD_BROADCAST_PERMISSION, message);
    }

    @Override
    public void broadcastMessage(String permission, Object... message) {
        ChatArguments args = new ChatArguments(message);
        for (PermissionsSubject player : getAllWithNode(permission)) {
            if (player instanceof CommandSource) {
                ((CommandSource) player).sendMessage(args);
            }
        }
    }

    @Override
    public void broadcastMessage(ChatChannel chatChannel, Object... message) {
        chatChannel.broadcastToReceivers(new ChatArguments(message));
    }

    @Override
    public Player getPlayer(String name, boolean exact) {
        name = name.toLowerCase();
        if (exact) {
            for (Player player : players.getValues()) {
                if (player.getName().equalsIgnoreCase(name)) {
                    return player;
                }
            }
            return null;
        } else {
            return StringUtil.getShortest(StringUtil.matchName(players.getValues(), name));
        }
    }

    @Override
    public Collection<Player> matchPlayer(String name) {
        return StringUtil.matchName(Arrays.<Player>asList(getOnlinePlayers()), name);
    }

    @Override
    public AccessManager getAccessManager() {
        return accessManager;
    }

    @Override
    public FileSystem getFilesystem() {
        return filesystem;
    }
}