Java tutorial
/* * 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); } } } }