/** * @(#)gnsd.java 1.0.1 98/12/20 *

* Copyright (C) 1998 David E. Wexler *

* 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 2 * of the License, or (at your option) any later version. See * license.txt for a full copy of the GNU GPL. *

* 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. *

* To Contact the author send e-mail to vagabond@netdragon.com or send * snailmail to: 511 Bobcat Ct, Punta Gorda, FL 3398. */ import java.net.*; import java.io.*; import java.util.*; import logger; import Group; /** * gnsd - Controls server level functions such as group control, user count, * *

* Loads cofinguration file, and starts the primary server thread that * waits for user connection. A new user thread is spawned for every * open connection. Also handls joining of groups, lists, and user * count. * * @author David Wexler (vagabond@netdragons.com) * @version 1.0.1 * @since gnsd.1.0.1 */ public class gnsd { /** * Used to log major commands to PATH/log/access_log. This * will log @connect, @login, @join, @joinnew, @list, @part, and @logout. * Logs are timestamped via time_t, information about the user's host, * command, and other useful information is given. */ private logger log; /** * Used to manage incoming client connections. */ private Socket client; /** * Stores information about where data files are located. Read from the * configuration file. * * @see #loadConf(java.lang.String) */ private String PATH; /** * Stores information about the activate port. Default value is 5000, can * be changed through the configuration file. * * @see #loadConf(java.lang.String) */ private int PORT = 5000; /** * Store information about how many port to queue. Default value is 512, * can be changed through the configuration file. * * @see #loadConf(java.lang.String) */ private int QUEUE = 512; /** * Tells the server to Output debug information. Useful when you are * trying to build a new game, or just make sure the server is working. * Default value is false, can be changed through the configuration file. * * @see #loadConf(java.lang.String) */ private boolean DEBUG = false; /** * Stores how many users are on-line currently. */ private int userCount = 0; /** * Stores information about the current groups. */ private static Vector groups = new Vector(); /** * Starts the General Network Server. This is the primary working function, * it loads all configuration variables, starts the server and waits for * new connections. * * @param files Configuration file to be loaded. * @see #loadConf(java.lang.String) * @since gnsd.1.0.1 */ public gnsd(String file) throws IOException { loadConf(file); if (PATH == null) System.out.println("* > ERROR: PATH Not defined"); else { System.out.println("* > gnsd: General Network Sever open on port " + PORT); setLogger(new logger(DEBUG, this)); ServerSocket server = new ServerSocket(PORT, QUEUE); while (true) { client = server.accept(); log.print(client.getInetAddress() + " @connect 100"); User u = new User(client, log, this); u.start(); System.gc(); } } } /** * Tells the Application load. This functions makes sure that we have * the proper number of arugments (0 or 1). If we don't have any arugments * the configure files is set to ./gnsd.conf. If an arugment is * given, we use that as the configure file. * * @since gnsd.1.0.1 */ public static void main(String args[]) throws IOException { if (args.length > 1) throw new RuntimeException ("Syntax: gnsd "); else { System.out.println("*** Starting General Network Server v.1.0.0 ***"); System.out.println("*** http://www.netdragons.com/games/24/gns/ ***"); if (args.length == 0) new gnsd("./gnsd.conf"); else new gnsd(args[0]); } } /** * Sets which log class we should use. * * @since gnsd.1.0.1 */ public final void setLogger(logger l) { log = l; } /** * Returns the servers PATH. This is used by sub classes in order to * maintain an acurate data without having to hardcode information. * * @since gnsd.1.0.1 */ public final String getPath() { return PATH; } /** * Function is used to manage the user count. If you call the function * with a +1, a user is added to the count, call it with a -1, * and a user is substracted. * * @param i How many users to add to the count. * @since gnsd.1.0.1 */ public void userCount(int i) { userCount += i; } /** * Gets how many users are currently on-line. * * @return number of users currently on-line. * @since gnsd.1.0.1 */ public int getUserCount() { return userCount; } /** * Returns a list of groups back to the user based on their gameID. * Designed to list all the channels that the player can join and return it * to the user one line at a time. Data sent back to the user is, groupID, * title, mode, and user count. * * @param gID the game ID to match on * @param u Instance of the user so we can send data. * @since gnsd.1.0.1 */ public void getList(int gID, User u) { Enumeration e = groups.elements(); log.print(u.getAddress() + " @list("+ gID + ")" + u.getID() + " 100"); while (e.hasMoreElements ()) { Group g = (Group) e.nextElement(); if (g.checkGameID(gID)) u.send("@|list|" + g.getListData()); } } /** * Checks to see if a group is already formed. This function is used when * the user trys to join a channel. If the channel does not exist, * the @joinnew command must be used. If the @joinnew command wased used * and the channel exists, it's just like using @join. * * @param cID id to check * @since gnsd.1.0.1 */ public boolean checkGroup(int cID) { Enumeration e = groups.elements(); while (e.hasMoreElements ()) { Group g = (Group) e.nextElement(); if (g.checkID(cID)) return true; } return false; } /** * Allows the user to authenticate to the server. Loads * PATH/users.dbf and checks to make sure that both the given user * name nad password are correct. users.dbf is a database file seperated * by a ",". Read fields are name, password, ID. * * @param uID the users ID number * @param uPassword the users password * @return the players name if ID and password were * correct, logout otherwise. * @since gnsd.1.0.1 */ public String authenticate(int uID, String uPassword) { try { StringTokenizer Token; String fName; String fPassword; int fID; FileInputStream file = new FileInputStream(PATH + "/users.dbf"); DataInputStream data = new DataInputStream(file); String line; while((line = data.readLine()) != null) { Token = new StringTokenizer(line, ",", false); fName = Token.nextToken(); fPassword = Token.nextToken(); if (toInt(Token.nextToken()) == uID) { if (fPassword.compareTo(uPassword) == 0) { file.close(); return fName; } else { file.close(); return "logout"; } } } file.close(); } catch (IOException ex) { System.err.println("* > ERROR: Could not read file " + PATH + "/users.dbf"); } return "logout"; } /** * Allows a user to join a group. Finds the channel the user request to * join and adds them to the list. If the channel does not exist, a new * "Network" channel is made, this is to handle the auto join feature. * * @param cID the ID of the group to join. * @param u Instance of the User. * @since gnsd.1.0.1 */ public void join(int cID, User u) { boolean found = false; Enumeration e = groups.elements(); while (e.hasMoreElements ()) { Group g = (Group) e.nextElement(); if (g.checkID(cID)) { log.print(u.getAddress() + " @join("+ cID + ") " + u.getID() + " 100"); u.join(g); found = true; break; } } if (!found) { joinNew(cID, u.getGameID(), "Primary Network Channel", "chan", u); } } /** * Will create a new group and allow a user to join it. Much like the join * function, except this will create a new group for users to join. * * @param cID group ID to create * @param gID game ID from client. * @param title title/topic of the group * @param mode information about the type of channel * @param u Instance of the User. * @see #join(int, User) * @since gnsd.1.0.1 */ public void joinNew(int cID, int gID, String title, String mode, User u) { log.print(u.getAddress() + " @joinnew("+ cID + ") " + u.getID() + " 100"); Group g = new Group(cID, gID, title, mode, this); groups.addElement(g); u.join(g); } /** * Will remove a group from the group vector. When a channel doesn't have * any more users in it this function allows it to be closed down. * * @param g the group to be closed. * @since gnsd.1.0.1 */ public void remove(Group g) { groups.removeElement(g); System.gc(); } /** * Allows the server to be configured at runtime. There are 4 options that * can be set, PATH, PORT, QUEUE, and DEBUG. To set an option, simple use * It's name, a space, and the value of the option, such as PORT 4000. When * you run the General Network Server, you will see all the runtime options * that are set. * * @param f file to load. * @since gnsd.1.0.1 */ private void loadConf(String f) { System.out.println("* > gnsd: loading " + f); StringTokenizer Token; try { FileInputStream file = new FileInputStream(f); DataInputStream data = new DataInputStream(file); String line; String option; while((line = data.readLine()) != null) { Token = new StringTokenizer(line, " ", false); option = Token.nextToken(); if (option.compareTo("PATH") == 0) { PATH = Token.nextToken(); System.out.println("* > gnsd: PATH = " + PATH); } else if (option.compareTo("PORT") == 0) { PORT = toInt(Token.nextToken()); System.out.println("* > gnsd: PORT = " + PORT); } else if (option.compareTo("QUEUE") == 0) { QUEUE = toInt(Token.nextToken()); System.out.println("* > gnsd: QUEUE = " + QUEUE); } else if (option.compareTo("DEBUG") == 0) { if (Token.nextToken().compareTo("yes") ==0) DEBUG = true; else DEBUG = false; System.out.println("* > gnsd: DEBUG = " + DEBUG); } } } catch (IOException ex) { System.err.println("* > ERROR: Could not config file"); } } /** * Converts a String to an int; * * @param s The String to be converted * @return an int value from s * @since gnsd.1.0.1 */ private int toInt(String s) { Integer i = new Integer(s); return i.intValue(); } }