org.parakoopa.udphp.mediator.Mediator.java Source code

Java tutorial

Introduction

Here is the source code for org.parakoopa.udphp.mediator.Mediator.java

Source

/*
 * Copyright (C) 2014 Parakoopa and HappyTear Studios
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.parakoopa.udphp.mediator;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;

/**
 * This class starts the UDP and TCP listening sockets.
 *
 * @author Parakoopa
 */
public class Mediator {

    /**
     * * (DEFAULT) SETTINGS ** 
     */
    /**
     * --port
     * The port to listen on. Use the command line argument --port to change it.
     */
    private static int port = 6510;

    /**
     * --quiet
     * Whether or not the console output should be hidden.
     */
    private static boolean quiet = false;

    /**
     * --verbose
     * Log additional info?
     */
    private static boolean verbose = false;

    /**
     * --log
     * Logfile (or null)
     */
    private static String log = null;

    /**
     * --disable-lobby
     * Is the lobby enabled?
     */
    private static boolean lobby = true;

    /**
     * Object that represents a server.
     * Contains TCP socket, port, and the 5 data-strings
     */
    private HashMap<String, Server> serverMap;
    /**
     * Object that represents a client
     * Contains only port right now.
     */
    private HashMap<String, Client> clientMap;
    /**
     * TCP Server.
     */
    private ServerSocket server;
    /**
     * UDP Server.
     */
    private DatagramSocket server_udp;
    /**
     * Recieving buffer for UDP.
     */
    byte[] receiveData = new byte[1024];

    /**
     * Command-line main-method. Currently no command line paramters are
     * supported.
     *
     * @param args Not used.
     */
    public static void main(String[] args) {

        /* Setup command line args */
        String header = "Start the mediation server for your Game Maker game that uses either UDPHP "
                + "or the HappyTear Multiplayer Engine PLUS.";

        Options options = new Options();
        options.addOption(OptionBuilder.withLongOpt("port")
                .withDescription(
                        "Specify the TCP and UDP port this server will listen on. Default: " + Mediator.port)
                .hasArg().withArgName("PORT").create("p"));
        options.addOption("h", "help", false, "Print this help text.");
        options.addOption(OptionBuilder.withLongOpt("disable-lobby")
                .withDescription("Ignore all requests of listing the connected servers.").create());
        options.addOption("q", "quiet", false, "Don't output anything to the console.");
        options.addOption("v", "verbose", false, "Print and/or log all information.");
        options.addOption(OptionBuilder.withLongOpt("log")
                .withDescription("Log output to this file. Will still log" + " even if 'quiet' is set.").hasArg()
                .withArgName("FILE").create("l"));

        try {
            CommandLineParser parser = new BasicParser();
            CommandLine line = parser.parse(options, args);
            if (line.hasOption("help")) {
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp("java -jar udphp_med.jar", header, options, "", true);
                System.exit(0);
            }
            if (line.hasOption("quiet")) {
                Mediator.quiet = true;
            }
            if (line.hasOption("verbose")) {
                Mediator.verbose = true;
            }
            if (line.hasOption("log")) {
                Mediator.log = line.getOptionValue("log");
            }
            if (line.hasOption("port")) {
                Mediator.port = Integer.valueOf(line.getOptionValue("port"));
            }
            if (line.hasOption("disable-lobby")) {
                Mediator.lobby = false;
            }
        } catch (ParseException ex) {
            Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, null, ex);
        }
        /* END Setup command line args */

        new Mediator();
    }

    public Mediator() {
        try {
            //Set up some local variables
            server = new ServerSocket(port);
            server_udp = new DatagramSocket(port);
            serverMap = new HashMap();
            clientMap = new HashMap();
            final Mediator me = this;

            Mediator.log("UDPHP STARTED", false);
            Mediator.log("Starting UDP and TCP servers on port " + port, false);

            //Start two new threads for the servers, just to be on the safe side.
            new Thread() {
                @Override
                //START UDP SERVER
                public void run() {
                    Mediator.log("-> UDP: Loaded UDP Listener", true);
                    //When packet is processed: Continue with next packet
                    while (true) {
                        try {
                            //Create incoming packet and wait for it.
                            DatagramPacket packet = new DatagramPacket(receiveData, receiveData.length);
                            server_udp.receive(packet);
                            //When a packet arrivied: Deal with it. 
                            UDPPacket packetHandler = new UDPPacket(me, packet, server_udp);
                            //This was once also a seperate thread, that's why the method is called run.
                            //annother thread is not needed though.
                            //it is propably even more efficient to just swap the packet out instead of
                            //creating a new class above. Do what you want :)
                            packetHandler.run();
                        } catch (IOException ex) {
                            //Print all exceptions.
                            ex.printStackTrace();
                        }
                    }
                }
            }.start();
            new Thread() {
                @Override
                //START TCP SERVER
                public void run() {
                    Mediator.log("-> TCP: Loaded TCP Listener", true);
                    //When connection thread is created: Wait for next connection
                    while (true) {
                        try {
                            //Wait for connection
                            Socket client = server.accept();
                            //When connection is opened: Start thread that handles it.
                            TCPConnection connectionHandler = new TCPConnection(me, client, server);
                            new Thread(connectionHandler).start();
                        } catch (IOException ex) {
                            //Print all exceptions.
                            ex.printStackTrace();
                        }
                    }
                }
            }.start();
        } catch (IOException ex) {
            //Print all exceptions.
            ex.printStackTrace();
        }
    }

    /**
     * Returns server HashMap.
     * Each server object contains TCP socket, port, and the 5 data-strings
     *
     * @return HashMap containing the server objects
     */
    public HashMap<String, Server> getServerMap() {
        return serverMap;
    }

    /**
     * Returns client HashMap.
     * Each client object contains port
     *
     * @return HashMap containing the client objects
     */
    public HashMap<String, Client> getClientMap() {
        return clientMap;
    }

    /**
     * Get the server object that represents the ip (and create it if it doesn't exist)
     * @param ip IP of the server
     * @return server object with that ip
     */
    public Server getServer(String ip) {
        Server serverObj;
        if (serverMap.containsKey(ip)) {
            serverObj = serverMap.get(ip);
        } else {
            serverObj = new Server(ip);
            serverMap.put(ip, serverObj);
        }
        return serverObj;
    }

    /**
     * Get the client object that represents the ip (and create it if it doesn't exist)
     * @param ip IP of the client
     * @return client object with that ip
     */
    public Client getClient(String ip) {
        Client clientObj;
        if (clientMap.containsKey(ip)) {
            clientObj = clientMap.get(ip);
        } else {
            clientObj = new Client();
            clientMap.put(ip, clientObj);
        }
        return clientObj;
    }

    /**
     * Remove this IP from the server  list.
     * @param hostAddress 
     */
    void destroyServer(String ip) {
        serverMap.remove(ip);
    }

    /**
     * Remove this IP from the client list.
     * @param hostAddress 
     */
    void destroyClient(String ip) {
        clientMap.remove(ip);
    }

    /**
     * Recieving buffer for UDP.
     *
     * @return A buffer.
     */
    public byte[] getReceiveData() {
        return receiveData;
    }

    public static boolean isLobby() {
        return lobby;
    }

    /**
     * Logs to the console (if quiet was not set) and to the logfile if specified
     * The logfile will also get timestamps for each event.
     * @param str String to log
     * @param verbose boolean Should this be logged only with --verbose?
     */
    public static void log(String str, boolean verbose) {
        //Don't print verbose lines if not requested
        if (verbose && !Mediator.verbose)
            return;
        /* CONSOLE OUTPUT */
        if (!Mediator.quiet) {
            System.out.println(str);
        }
        /* FILE LOG */
        if (Mediator.log != null) {
            DateFormat date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT,
                    Locale.getDefault());
            try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(Mediator.log, true)))) {
                out.println(date.format(new Date()) + " : " + str);
            } catch (IOException ex) {
                Logger.getLogger(Mediator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}