realityshard.container.ContainerFacade.java Source code

Java tutorial

Introduction

Here is the source code for realityshard.container.ContainerFacade.java

Source

/**
 * For copyright information see the LICENSE document.
 */

package realityshard.container;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import realityshard.container.gameapp.GameAppManager;
import realityshard.container.gameapp.GameAppContext;
import realityshard.container.gameapp.GameAppFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import realityshard.container.network.GameAppContextKey;
import realityshard.container.util.Handle;
import realityshard.container.util.HandleRegistry;

/**
 * Container facade is what the host application works with.
 * Creates all game apps and manages them.
 * Also manages the network.
 *
 * @author _rusty
 */
public final class ContainerFacade implements GameAppManager {

    private static final class GameAppInfo {
        public GameAppFactory Factory;
        public MetaGameAppContext MetaContext;
        public NioEventLoopGroup Boss;
        public NioEventLoopGroup Worker;
        public NioServerSocketChannel NetworkChannel;
    }

    private final static Logger LOGGER = LoggerFactory.getLogger(ContainerFacade.class);

    private final Map<String, GameAppInfo> gameApps = new ConcurrentHashMap<>();
    private final HandleRegistry<GameAppContext> gameAppHandleRegistry = new HandleRegistry<>();

    private InetAddress localAddress = null;

    /**
     * Constructor.
     * 
     * @param       factories               The factories for each kind of game app. 
     */
    public ContainerFacade(List<GameAppFactory> factories) throws Exception {
        for (GameAppFactory factory : factories) {
            gameApps.put(factory.getName(), produceInfoFromFactory(factory));
        }

        // now start up all start-up apps
        startUpApps();

        // try to get the local ip
        // TODO: use a better method ;)
        Enumeration<NetworkInterface> netIfaces = NetworkInterface.getNetworkInterfaces();
        while (netIfaces.hasMoreElements()) {
            NetworkInterface cur = netIfaces.nextElement();
            if (!cur.isUp() || cur.isLoopback() || cur.isVirtual()) {
                continue;
            }

            Enumeration<InetAddress> addr = cur.getInetAddresses();
            while (addr.hasMoreElements()) {
                InetAddress curAddr = addr.nextElement();
                if (curAddr instanceof Inet4Address) {
                    localAddress = curAddr;
                    return;
                }
            }
        }
    }

    /**
     * Check if we can create a game app with a certain name
     * 
     * @param       name
     * @return      True if we got a factory for it, false otherwise
     */
    @Override
    public boolean canCreateGameApp(String name) {
        return gameApps.containsKey(name);
    }

    /**
     * Create a game app by name.
     * 
     * @param       name
     * @param       parent
     * @param       additionalParams
     * @return      The game app or null, if creation failed.
     */
    @Override
    public Handle<GameAppContext> createGameApp(String name, Handle<GameAppContext> parent,
            Map<String, String> additionalParams) {
        return internalCreateGameApp(name, parent, additionalParams);
    }

    /**
     * Try get a game app handle by its unique identifier.
     * 
     * @param       gameAppUid
     * @return      The global handle of the game app, or null.
     */
    @Override
    public Handle<GameAppContext> tryGetGameApp(UUID gameAppUid) {
        return gameAppHandleRegistry.getHandle(gameAppUid);
    }

    /**
     * Shutdown a specific game app.
     * 
     * @param       that                    Game app
     */
    @Override
    public void removeGameApp(Handle<GameAppContext> that) {
        GameAppInfo gameAppInfo = gameApps.get(that.get().getName());

        if (gameAppInfo == null) {
            LOGGER.error("Game app doesnt exist! [name {} ]", that.get().getName());
            return;
        }

        gameAppInfo.MetaContext.shutdown(that.get());
    }

    /**
     * Getter.
     * 
     * @param       that
     * @return      The local address of that game app.
     */
    @Override
    public InetSocketAddress localAddressFor(Handle<GameAppContext> that) {
        GameAppInfo gameAppInfo = gameApps.get(that.get().getName());

        if (gameAppInfo == null) {
            LOGGER.error("Game app doesnt exist! [name {} ]", that.get().getName());
            return null;
        }

        int port = gameAppInfo.NetworkChannel.localAddress().getPort();

        // always return the address of this server...
        // the parameter is important when we do remoting
        if (localAddress == null) {
            return new InetSocketAddress(Inet4Address.getLoopbackAddress(), port);
        }

        return new InetSocketAddress(localAddress, port);
    }

    /**
     * Load all apps that have the "start-up" marker
     */
    private void startUpApps() {
        for (GameAppInfo gameAppInfo : gameApps.values()) {
            if (!gameAppInfo.Factory.isStartup()) {
                continue;
            }

            internalCreateGameApp(gameAppInfo.Factory.getName(), null, new HashMap<String, String>());
        }
    }

    /**
     * Shutdown this container.
     */
    public void shutdown() {
        for (Map.Entry<String, GameAppInfo> entry : gameApps.entrySet()) {
            GameAppInfo gameAppInfo = entry.getValue();

            gameAppInfo.MetaContext.shutdown();
            gameAppInfo.NetworkChannel.close().syncUninterruptibly();
        }
    }

    /**
     * Init our factories and load the info about them into our map.
     */
    private GameAppInfo produceInfoFromFactory(GameAppFactory factory) throws Exception {
        GameAppInfo result = new GameAppInfo();
        result.Factory = factory;
        result.MetaContext = new MetaGameAppContext(factory.getName(), this);

        // register the metacontext with its own aggregator
        result.MetaContext.getEventAggregator().register(result.MetaContext);

        result.Boss = new NioEventLoopGroup();
        result.Worker = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(result.Boss, result.Worker).channel(NioServerSocketChannel.class)
                .childAttr(GameAppContextKey.KEY, result.MetaContext).childAttr(GameAppContextKey.IS_SET, false)
                .option(ChannelOption.SO_BACKLOG, 1000);

        result.NetworkChannel = (NioServerSocketChannel) factory.getServerChannel(bootstrap);

        return result;
    }

    /**
     * Create a new game app, using the factory.
     */
    private Handle<GameAppContext> internalCreateGameApp(String name, Handle<GameAppContext> parent,
            Map<String, String> additionalParams) {
        GameAppInfo gameAppInfo = gameApps.get(name);

        if (gameAppInfo == null) {
            LOGGER.error("Game app doesnt exist! [name {} ]", name);
            return null;
        }

        // create the context for the app
        GameAppContext context = new GameAppContext.Default(name, this, parent);

        // register it
        Handle<GameAppContext> contextHandle = gameAppHandleRegistry.register(context);

        // create the app
        if (!gameAppInfo.Factory.initGameApp(contextHandle, parent, additionalParams)) {
            LOGGER.error("Failed to create game app! [name {} ]", gameAppInfo.Factory.getName());
            contextHandle.invalidate();
            return null;
        }

        // dont forget to add it to the metacontext
        gameAppInfo.MetaContext.addContext(context);

        return contextHandle;
    }
}