/**
* @(#)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();
}
}