/* ** j###t ########## #### #### ** j###t ########## #### #### ** j###T "###L J###" ** ######P' ########## ######### ** ######k, ########## T######T ** ####~###L #### ** #### q###L ########## .##### ** #### \###L ########## #####" */ package key; import key.primitive.Gender; import java.io.*; import java.util.Date; import key.config.*; import key.util.Trie; /** * Is the main instance of a player on the program. The * authenticate() routine has been changed in player to * attempt to stop a player being swapped out while they * connect, but no guarantee's... we should probably * be using tags in here somewhere. * * It is the responsibility of this class to send the opening * logo, and retrieve a player from the name entered. (Authenticating * the player or creating a suitable player as necessary). No further * action need be taken. */ public class ConnectingPlayer implements LatentlyCached, Interactive { InteractiveConnection ic; Player p = null; Player created = null; public ConnectingPlayer() { } public void run( InteractiveConnection connection ) { Key key = Key.instance(); Container shortcuts = (Container) Key.shortcuts(); ConnectionConfiguration connectConfig = (ConnectionConfiguration) key.getConfig().getElement( "connection" ); ic = connection; outputLogo(); Player p = null; try { String name; int tries = 0; Key.getLatencyCache().addToCache( this ); // loop this continually until they // managed to get a name correct do { name = getPlayersName( ic ); // the player probably typed 'quit', this // will clean it up. if( name == null ) break; ic.blankLine(); ic.blankLine(); p = getPlayerFromName( ic, name ); } while( p == null && ic.isConnected() && ++tries < 3 ); if( p == null || !ic.isConnected() || tries >= 3 ) { if( p != null ) { p.endConnect(); Registry.instance.deleteIfTemporary( p ); p = null; } ic.flush(); ic.close(); return; } else { connectToProgram( ic, p ); // kick off the player (they'll look at the motd // & link themselves in when they're ready) ic.interactWith( p ); p.endConnect(); p = null; } } catch( java.io.IOException e ) { Log.debug( this, "disconnect from login" ); ic.close(); if( p != null ) { p.endConnect(); Registry.instance.deleteIfTemporary( p ); p = null; } } catch( NetworkException e ) { if( ic instanceof SocketIC ) Log.debug( this, "disconnect from login [" + ((SocketIC)ic).getAddress() + "]" ); else Log.debug( this, "disconnect from login" ); ic.close(); if( p != null ) { p.endConnect(); Registry.instance.deleteIfTemporary( p ); p = null; } } catch( Exception e ) { Log.debug( this, e.toString() + " at login prompt" ); ic.send( "\n\nWe apologise, but an error has occurred attempting to connect you. Please mail forest@realm.progsoc.uts.edu.au if you are continually having problems. Detail: " + e.toString() ); ic.flush(); ic.close(); if( p != null ) { p.endConnect(); Registry.instance.deleteIfTemporary( p ); p = null; } } finally { Key.getLatencyCache().removeFromCache( this ); } } /** * If a players name is not matched to a character on * the program, this routine leads them through the * operations in creating a character of their own. * * @return null if an error or cancellation occurs */ protected Player connectGuest( InteractiveConnection ic, String name ) throws IOException { Container online = Key.instance().getOnline(); Container shortcuts = (Container) Key.shortcuts(); ConnectionConfiguration connectConfig = (ConnectionConfiguration) Key.instance().getConfig().getElement( "connection" ); Container players = Key.instance().getResidents(); // check to ensure there isn't a newbie ban on this // site. if( !ic.newbiesAllowed() ) { Screen newbieban = (Screen) online.getElement( "newbieban" ); if( newbieban == null ) ic.sendFeedback( "New players are not currently permitted to connect from this site" ); else ic.send( newbieban.text, false ); ic.flush(); ic.close(); return null; } // check that the name is not in the reserved list // (names that aren't permitted to log in) StringSet ss = (StringSet) online.getElement( "reservedNames" ); if( ss != null && ss.get( name ) != null ) { ic.send( "'" + name + "' means something else here, please choose another." ); ic.blankLine(); ic.blankLine(); return( null ); } // need to create a new player & link it in ic.send( " '" + name + "' not found in player database...\n" ); if( guestConfirmName( ic, name ) ) { Player created = null; try { created = (Player) Factory.makeAtom( Player.class, name ); created.setKey( name ); created.beginConnect(); players.add( created ); Rank nr = Key.instance().getInitialRank(); if( nr != null ) nr.add( created ); Atom a = (Atom) shortcuts.getExactElement( name ); if( a != null ) { Paragraph disclaimer = (Paragraph) connectConfig.getProperty( "disclaimer" ); if( disclaimer != null && !disclaimer.isEmpty() ) { ic.send( disclaimer, false ); if( !Grammar.getYesNo( "Do you accept these conditions? ", true, ic ) ) { ic.flush(); ic.close(); if( created != null ) { players.remove( created ); Registry.instance.deleteIfTemporary( created ); created.endConnect(); return null; } } } Paragraph gender = (Paragraph) connectConfig.getProperty( "genderRequest" ); if( gender != null && !gender.isEmpty() ) { ic.blankLine(); ic.send( gender, false ); ic.blankLine(); boolean genderOk; do { genderOk = true; String in = ic.input( "Enter 'm' or 'f': " ); if( in.equalsIgnoreCase( "m" ) ) created.setGender( Gender.MALE_GENDER ); else if( in.equalsIgnoreCase( "f" ) ) created.setGender( Gender.FEMALE_GENDER ); else { genderOk = false; if( in.startsWith( "\'" ) ) ic.send( "\nDo not type the quotes, just the letter m or f.\n" ); else ic.send( "\nPlease enter, literally: 'm' if you are male, or 'f' if you are female.\n" ); } } while( !genderOk ); } Paragraph newbieScreen = (Paragraph) connectConfig.getProperty( "newbieA" ); if( newbieScreen != null && !newbieScreen.isEmpty() ) { ic.send( newbieScreen, false ); Grammar.getReturn( ic ); } newbieScreen = (Paragraph) connectConfig.getProperty( "newbieB" ); if( newbieScreen != null && !newbieScreen.isEmpty() ) { ic.send( newbieScreen, false ); Grammar.getReturn( ic ); } Log.log( "newbie", name + " from " + ((SocketIC)ic).getAddress() ); return( created ); } else ic.send( "Cannot create a player with this name" ); } catch( BadKeyException e ) { ic.send( "Your name must only contain alphabetical characters" ); } catch( NonUniqueKeyException e ) { ic.send( "There is already another player using this name" ); } catch( IOException e ) { if( created != null ) { try { players.remove( created ); } catch( NonUniqueKeyException ex ) { } catch( BadKeyException ex ) { } created.endConnect(); Registry.instance.deleteIfTemporary( created ); created = null; } } } else { ic.blankLine(); ic.blankLine(); ic.send( " Perhaps someone else, then..." ); ic.blankLine(); } return( null ); } protected Player connectResident( InteractiveConnection ic, Player p ) throws IOException { p.beginConnect(); // now check if the player has been banished if( p.isBanished() ) { Screen banished = (Screen) Key.instance().getOnline().getElement( "playerBanished" ); if ( banished == null ) { ic.sendFeedback( "Your character is currently banished." ); } else ic.send( banished.text, false ); p.endConnect(); p = null; ic.flush(); ic.close(); return null; } int tries = 0; boolean name_okay = false; try { do { if( p.authenticate( ic ) ) { name_okay = true; } else { name_okay = false; } } while( !name_okay && ++tries < 3 ); } catch( PasswordEntryCancelled except ) { ic.send( " Perhaps someone else, then..." ); p.endConnect(); p = null; return( null ); } if( name_okay ) { // the endConnect is handled by the // level above. //p.endConnect(); // should return a tag instead? return( p ); } else { ic.flush(); ic.close(); p.endConnect(); p = null; return( null ); } } protected void connectToProgram( InteractiveConnection ic, Player p ) throws IOException { // copyright messages ic.send( Key.copyright ); ic.sendLine(); // the MOTD Screen motd = (Screen) Key.instance().getOnline().getElement( "motd" ); if( motd != null && !motd.isEmpty() && (p.loginStats.lastConnection == null || motd.isModifiedAfter( p.loginStats.lastConnection ) ) ) { ic.send( motd.text, false ); Grammar.getReturn( ic ); } // the clan motd { Clan c; c = p.getClan(); if( c != null ) { motd = c.motd; if( motd != null && !motd.isEmpty() && (p.loginStats.lastConnection == null || motd.isModifiedAfter( p.loginStats.lastConnection ) ) ) { ic.send( motd.text, false ); Grammar.getReturn( ic ); } } } // link this player into the program try { synchronized( p ) { if( p.connected() ) { p.getConnection().sendSystem( "Reconnecting..." ); p.disconnect(); } p.connectTo( ic ); } } catch( NonUniqueKeyException e ) { ic.send( "This player is already on the program. You will need to enter another name. (Internal error)" ); throw new UnexpectedResult( e.toString() ); } catch( BadKeyException e ) { ic.send( "Your name must consist of alphabetical characters only. Please try again. (Internal error)" ); throw new UnexpectedResult( e.toString() ); } } /** * Retrieves a player structure from the entered name. Should * the name not be acceptable for some reason, null is returned. * Should a serious error occur, the connection will be closed, * also. * * @return null on failure */ protected Player getPlayerFromName( InteractiveConnection ic, String name ) throws IOException { Object a = Key.shortcuts().getExactElement( name ); if( a == null ) return( connectGuest( ic, name ) ); else { if( a instanceof Player ) { Player p = (Player) a; if( p.willSync() ) return( connectResident( ic, p ) ); else ic.send( "That name is in use by another new player at the moment, and can't be used." ); } else ic.send( "That name means something else here." ); } return( null ); } /** * Puts the initial logo on the players screen. */ protected void outputLogo() { Screen logo = (Screen) Key.instance().getOnline().getElement( "logo" ); if( logo != null ) ic.send( logo.text, false ); } /** * This function returns the player's name, as entered at * the 'Please enter your name:' prompt. */ protected String getPlayersName( InteractiveConnection ic ) throws IOException { boolean name_okay; String name; do { name_okay = false; name = ic.input( "Please enter your name: " ); if( name.length() < Player.MIN_NAME ) { ic.send( "We require that your name is at least " + Player.MIN_NAME + " characters long. You'll need to choose another." ); continue; } else if( name.length() > Player.MAX_NAME ) { ic.send( "We require that your name is less than " + Player.MAX_NAME + " characters long. You'll need to choose another." ); continue; } if( !Grammar.isStringCompletelyAlphabetical( name ) ) { ic.send( "Your name must only contain alphabetical characters" ); continue; } if( name.equalsIgnoreCase( "quit" ) ) { ic.flush(); ic.close(); return null; } else if( name.equalsIgnoreCase( "who" ) ) { ic.blankLine(); ic.blankLine(); key.commands.Who.displayTable( ic, Key.instance().players() ); ic.blankLine(); } else name_okay = true; } while( !name_okay ); return( name ); } /** * This function outputs the 'are you sure you want to use this * name' prompt, when someone starts to create a new player. * * @param ic the connection * @param name the name the player originally typed * @return true if the person replied 'yes' */ protected boolean guestConfirmName( InteractiveConnection ic, String name ) throws IOException { ConnectionConfiguration connectConfig = (ConnectionConfiguration) Key.instance().getConfig().getElement( "connection" ); ic.send( (Paragraph) connectConfig.getProperty( "newbieConfirm" ) ); ic.blankLine(); // output the multiple matches screen Object o; o = Key.instance().getResidents().getTrieFor( name.substring( 0, 3 ) ); if( (o != null) ) { if( o instanceof Trie ) ic.send( new TextParagraph( TextParagraph.CENTERALIGNED, ((Trie)o).contents(), 10, 10, 0, 0 ), false ); else if( o instanceof Reference ) ic.send( new TextParagraph( TextParagraph.CENTERALIGNED, ((Reference)o).getName(), 10, 10, 0, 0 ), false ); } else ic.send( new TextParagraph( TextParagraph.CENTERALIGNED, "<no close names found>", 10, 10, 0, 0 ), false ); ic.blankLine(); return( Grammar.getYesNo( "Do you still wish to create a new character of the name '" + name + "'? ", false, ic ) ); } public InteractiveConnection getInteractiveConnection() { return( ic ); } private boolean mod = true; public boolean modified() { return( mod ); } public void resetModify() { mod = false; } public synchronized void deallocate() { ic.send( "\n\nYou have taken too long to connect." ); ic.flush(); ic.close(); } }