/*
** j###t ########## #### ####
** j###t ########## #### ####
** j###T "###L J###"
** ######P' ########## #########
** ######k, ########## T######T
** ####~###L ####
** #### q###L ########## .#####
** #### \###L ########## #####"
**
*/
package key;
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
public class Main implements StartupHandler
{
/**
* Set to true if you want to halve your disk space usage
*/
public static final boolean COMPRESS_DISK_FILES = false;
public static final boolean CONNECT_TO_DATABASE = false;
/**
* Set to true if you want key to do automatic ident
* lookups
*/
public static final boolean SOCKET_IDENT_LOOKUP = true;
private File db;
private Key key;
private Registry registry;
private boolean adminMode;
public Main( boolean create, boolean admin )
{
Key.creation = create;
adminMode = admin;
}
public int doMenu( Menu m ) throws IOException
{
int choice;
do
{
System.err.println( m.title );
int i;
for( i = 0; i < m.options.length; i++ )
{
MenuOption mo = m.options[i];
System.err.println( Integer.toString( mo.number ) + "] " + mo.title );
}
System.err.println( "\n\n(enter displays this menu again)\nPlease select a menu option: " );
do
{
try
{
String s = getLine( "[1-" + i + "]: " );
if( s.length() > 0 )
{
choice = Integer.parseInt( s );
if( choice > i || choice <= 0 )
{
System.err.println( "Your choice must be between 1 and " + i + "." );
choice = -1;
}
}
else
choice = 0;
}
catch( NumberFormatException e )
{
System.err.println( "Please make your choice using digits only." );
choice = -1;
}
} while( choice < 0 );
} while( choice <= 0 );
return( choice );
}
public boolean findDB()
{
db = new File( "database" );
return( db.exists() );
}
public boolean load()
{
if( db.exists() )
{
Registry.baseDirectory = Key.confirmedDirectory( "distinct" );
Log.bootLog( "Loading '" + db.getPath() + "'..." );
registry = (Registry) Factory.loadObject( db );
key = Key.instance();
key.initFieldCache();
return true;
}
return( false );
}
private Player getValidPlayer() throws IOException
{
Player p;
do
{
String name = getValidName();
p = (Player) key.getResidents().getElement( name );
if( p != null )
return( p );
System.err.println( "\nCould not find player '" + name + "'. Please enter a different name." );
}
while( true );
}
private File getValidFile() throws IOException
{
File f;
do
{
String name = getLine( "Filename: " );
f = new File( name );
if( f.exists() )
return( f );
System.err.println( "\nCould not find file '" + f.getAbsolutePath() + "'.\nPlease enter a different filename." );
}
while( true );
}
public void addRevoke() throws IOException
{
Player p = getValidPlayer();
if( p.isBeyond() )
{
p.maybeClearBeyond();
System.err.println( p.getName() + " no longer has the beyond priviledge." );
}
else
{
p.maybeSetBeyond();
System.err.println( p.getName() + " now has the beyond priviledge." );
}
p.sync();
}
public void doScript() throws IOException
{
System.err.println(
"\n\n" +
"Two things are required to execute a script. First, a player\n" +
"name to execute the script as, and second, the filename of the\n" +
"script itself." );
Player p = getValidPlayer();
File f = null;
File file = getValidFile();
FileIC fic = new FileIC( file );
runScript( p, file, fic );
}
public void doConsoleLogin() throws IOException
{
System.err.println(
"\n\n" +
"Enter a player name to execute the script as." );
Player p = getValidPlayer();
System.err.println(
"\nConsole Login for " + p.getName() + "...\n" +
"Press EOF <CONTROL-D on unix or CONTROL-Z on dos> to close the connection.\n" );
ConsoleIC cic = new ConsoleIC();
try
{
p.connectTo( cic );
cic.interactWith( p );
p.run( cic );
System.err.println( "Connection terminated." );
}
catch( BadKeyException e )
{
e.printStackTrace();
}
catch( NonUniqueKeyException e )
{
e.printStackTrace();
}
}
public void runScript( Player p, File file, FileIC fic ) throws IOException
{
System.err.println( "\nWould you like to watch the output of the script scroll past?" );
fic.setDisplay( yesNo() );
System.err.println( "\nExecuting script '" + file.getAbsolutePath() + "'..." );
try
{
p.connectTo( fic );
fic.interactWith( p );
p.run( fic );
System.err.println( "Script complete." );
}
catch( BadKeyException e )
{
e.printStackTrace();
}
catch( NonUniqueKeyException e )
{
e.printStackTrace();
}
catch( NetworkException e )
{
System.out.println( e.getMessage() );
}
catch( Error e )
{
e.printStackTrace();
}
}
public String getValidName() throws IOException
{
int len;
boolean con;
String name;
System.err.println();
do
{
con = false;
name = getLine( "Administrator name: " );
len = name.length();
if( len < Player.MIN_NAME )
{
System.err.println( "Key requires that names are at least " + Player.MIN_NAME + " characters long." );
con = true;
}
else if( len > Player.MAX_NAME )
{
System.err.println( "Key requires that names are less than " + Player.MAX_NAME + " characters long." );
con = true;
}
else if( !Grammar.isStringCompletelyAlphabetical( name ) )
{
System.err.println( "Names must contain only alphabetical characters." );
con = true;
}
else if( name.equalsIgnoreCase( "quit" ) )
{
System.err.println( "You wouldn't be able to log in with this name." );
con = true;
}
} while( con );
return( name );
}
public void create( Class cl ) throws IOException
{
Key.creation = true;
adminMode = true;
Registry.baseDirectory = Key.confirmedDirectory( "distinct" );
registry = new Registry();
key = (Key) Factory.makeAtom( cl );
key.initFieldCache();
// set up the initial admin player
System.err.println();
System.err.println(
"It is necessary to create a single administration account in order to\n" +
"allow you to set up the server. You will now be prompted for the name\n" +
"and password of this account." );
String name;
String password;
Player admin = null;
do
{
name = getValidName();
password = getLine( "Password (will echo): " );
System.err.println();
System.err.println( "Administrator name: " + name + ", password: " + password + ". Okay?" );
} while( !yesNo() );
System.err.println();
try
{
admin = (Player) Factory.makeAtom( Player.class, name );
admin.setPassword( password );
admin.maybeSetBeyond();
admin.canSave = true;
key.getResidents().add( admin );
admin.sync();
if( registry.getStorageTypeIndex( admin.index, admin.timestamp ) != Registry.STORAGE_LOADED )
{
System.err.println( "An error has occurred creating this character: it has not been sync'd." );
System.exit( 1 );
}
}
catch( BadKeyException e )
{
System.err.println( "The name you have selected is not appropriate: " + e.toString() );
System.exit( 1 );
}
catch( NonUniqueKeyException e )
{
System.err.println( "The name you have selected is in use for something else: " + e.toString() );
System.exit( 1 );
}
key.sync();
System.err.println( "Created new Administrator '" + name + "'." );
System.err.println();
File setupDirectory = new File( "setup" );
File basicSetup = new File( setupDirectory, "basic.ks" );
File standardSetup = new File( setupDirectory, "standard.ks" );
if( basicSetup.exists() && standardSetup.exists() )
{
int mo = doMenu( seedMenu );
switch( mo )
{
case 1:
case 2:
case 3:
File use;
if( mo == 1 )
use = basicSetup;
else if( mo == 2 )
use = standardSetup;
else
{
System.err.println();
use = getValidFile();
}
FileIC fic = new FileIC( use );
runScript( admin, use, fic );
break;
case 4:
return;
}
}
else
{
if( basicSetup.exists() || standardSetup.exists() )
{
System.err.println(
"Unfortunately, this distribution of key seems to be missing one of these\n" +
"ready-made setup scripts. They normally reside in the 'setup' directory\n" +
"of the distribution, and are called 'standard.ks' and 'basic.ks'." );
}
else
{
System.err.println(
"Unfortunately, Key is either not running in its distribution directory, or\n" +
"these scripts have been inadvertently erased." );
}
System.err.println(
"\n" +
"This means that it is impossible at this time to automatically seed the\n" +
"database: you will need to set it up by hand." );
}
// this code doesn't get executed if they choose to go
// straight to the administration menu from option 4, above
System.err.println(
"\n" +
"We will now proceed to the administration menu.\n" +
"Press any key to continue." );
getLine( "" );
}
public void boot()
{
//System.setSecurityManager( Security.instance );
key.start();
}
public void acceptControl( Main m )
{
try
{
if( findDB() )
{
if( load() )
{
if( !adminMode )
{
boot();
return;
}
}
}
else
{
System.err.println( "Database file '" + db.getPath() + "' does not exist." );
System.err.println();
System.err.println( "Would you like to store Key's files in '" + System.getProperty( "user.dir", "" ) + "'?" );
if( !yesNo() )
{
System.err.println();
System.err.println( "Please run this program from the directory you wish to store the files in." );
String home = System.getProperty( "user.home", null );
String sep = System.getProperty( "file.separator", null );
if( home != null && sep != null )
System.err.println( "For instance, you may wish to run this program from '" + home + sep + "key'." );
return;
}
else
System.err.println();
// at the moment, this is hard coded. we might
// change it in the future.
create( Forest.class );
}
while( adminMode )
{
switch( doMenu( mainMenu ) )
{
case 1:
adminMode = false;
System.err.println( "\n" );
break;
case 2:
addRevoke();
break;
case 3:
doScript();
break;
case 4:
doConsoleLogin();
break;
case 5:
System.err.println( "\n" );
key.shutdown();
System.exit( 0 );
break;
}
}
boot();
}
catch( Exception e )
{
e.printStackTrace();
}
}
public boolean yesNo() throws IOException
{
String yn = getLine( "(y/N): " );
if( yn.length() > 0 )
{
char c = yn.charAt( 0 );
if( c == 'y' || c == 'Y' )
return( true );
}
return false;
}
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
public String getLine( String prompt ) throws IOException
{
System.err.print( prompt );
System.err.flush();
return( br.readLine() );
}
public static void main( String args[] )
{
boolean admin = false;
boolean gui = false;
boolean creation = false;
for( int i = 0; i < args.length; i++ )
{
if( args[i].equalsIgnoreCase( "admin" ) )
admin = true;
else if( args[i].equalsIgnoreCase( "swing" ) )
gui = true;
else if( args[i].equalsIgnoreCase( "creation" ) )
creation = true;
else
{
System.err.println( "Unknown argument '" + args[i] + "'" );
System.err.println(
"Available arguments:\n" +
"\n" +
"\tadmin \tStarts the program in an interactive mode\n" +
"\tswing \tAllows program monitoring with a Swing GUI\n" +
"\tcreation\tAllows the program to create directories as needed\n" );
return;
}
}
System.err.println( Key.copyright );
System.err.println();
try
{
StartupHandler sh = null;
if( gui )
{
try
{
Class c = Class.forName( "key.swing.Startup" );
sh = (StartupHandler) c.newInstance();
}
catch( Exception e )
{
System.err.println( "Could not initialise swing interface:" );
e.printStackTrace( System.err );
}
}
Main m = new Main( creation, admin );
if( sh == null )
sh = m;
sh.acceptControl( m );
}
catch( ExceptionInInitializerError e )
{
System.out.println( e.toString() + ":" );
Throwable t = e.getException();
t.printStackTrace();
}
catch( Exception e )
{
System.out.println( e.toString() );
e.printStackTrace( System.out );
}
}
static MenuOption[] mainMenuOptions =
{
new MenuOption( 1, "Start Key" ),
new MenuOption( 2, "Add or Revoke Administrator priviledges" ),
new MenuOption( 3, "Execute a KeyScript" ),
new MenuOption( 4, "Log in on the console" ),
new MenuOption( 5, "Exit" )
};
static Menu mainMenu = new Menu( "\n\nKey Administration Menu\n", mainMenuOptions );
static MenuOption[] seedMenuOptions =
{
new MenuOption( 1, "Basic (commands and ranks only)" ),
new MenuOption( 2, "Standard (basic + sample room descriptions & help files)" ),
new MenuOption( 3, "A custom script" ),
new MenuOption( 4, "Exit to the administration menu" )
};
static Menu seedMenu = new Menu(
"A brand new Key database is generally fairly sparse and unusable. It is\n" +
"strongly recommended that you seed the initial database using one of the\n" +
"provided setup scripts.\n", seedMenuOptions );
}
class MenuOption
{
String title;
int number;
MenuOption( int n, String t )
{
number = n;
title = t;
}
}
class Menu
{
MenuOption[] options;
String title;
Menu( String t, MenuOption[] o )
{
title = t;
options = o;
}
}