/
changed/
package com.planet_ink.coffee_mud.system;

import java.io.*;
import java.util.*;
import java.sql.*;
import java.net.*;

import com.planet_ink.coffee_mud.utils.*;
import com.planet_ink.coffee_mud.interfaces.*;
import com.planet_ink.coffee_mud.common.*;

/*
   Copyright 2000-2005 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
public class TelnetSession extends Thread implements Session
{
	private int status=0;
	private Socket sock;
	private BufferedReader in;
	private PrintWriter out;
	private MOB mob;
	private boolean killFlag=false;
	private boolean needPrompt=false;
	private boolean afkFlag=false;
	private StringBuffer input=new StringBuffer("");
	private boolean waiting=false;
	private static final int SOTIMEOUT=300;
	private Vector previousCmd=new Vector();
	private String[] clookup=null;
	private String lastColorStr="";
	private String lastStr=null;
	private int spamStack=0;
	private int supports=0;
    private int dequeCounter=0;
	private Vector snoops=new Vector();

	private boolean lastWasCR=false;
	private boolean lastWasLF=false;
	private boolean suspendCommandLine=false;

	public long lastStart=System.currentTimeMillis();
	public long lastStop=System.currentTimeMillis();
	public long lastLoopTop=System.currentTimeMillis();
	public long lastBlahCheck=0;
	public long milliTotal=0;
	public long tickTotal=0;
	public long lastKeystroke=0;
	public long promptLastShown=0;

	private int termID = 0;	//1 = ANSI, 2 = SOUND/MUSIC, 4=MXP
	public int currentColor='N';
	public int lastColor=-1;
	private static int sessionCounter=0;
	public TelnetSession(Socket s, String introTextStr)
	{
		super("TelnetSession."+sessionCounter);
		++sessionCounter;
		sock=s;
		try
		{
			sock.setSoTimeout(SOTIMEOUT);
            OutputStream rawout=sock.getOutputStream();
            InputStream rawin=sock.getInputStream();
            rawout.write('\n');
            rawout.write('\n');
            rawout.write(TELNET_IAC);
            rawout.write(TELNET_WILL);
            rawout.write(TELNET_MXP);
            rawout.flush();


            //out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(rawout, "UTF-8")));
            //in = new BufferedReader(new InputStreamReader(rawin, "UTF-8"));
			out = new PrintWriter(new OutputStreamWriter(rawout,"iso-8859-1"));
			in = new BufferedReader(new InputStreamReader(rawin,"iso-8859-1"));

			if(introTextStr!=null) print(introTextStr);

		}
		catch(SocketException e)
		{
		}
		catch(IOException e)
		{
		}
	}

	public boolean supports(int TERMID)
	{
	    return (supports&TERMID)==TERMID;
	}


	public int currentColor(){return currentColor;}
	public long getTotalMillis(){ return milliTotal;}
	public long getIdleMillis(){ return System.currentTimeMillis()-lastKeystroke;}
	public long getTotalTicks(){ return tickTotal;}
    public int dequeCommand(){dequeCounter++; return dequeCounter;}

	public long lastLoopTime(){ return lastLoopTop;}

	public MOB mob(){return mob;}
	public void setMob(MOB newmob)
	{ mob=newmob;}
	public int getWrap(){return ((mob!=null)&&(mob.playerStats()!=null))?mob.playerStats().getWrap():78;}
	public boolean killFlag(){return killFlag;}
	public void setKillFlag(boolean truefalse){killFlag=truefalse;}
	public Vector previousCMD(){return previousCmd;}
	public void startBeingSnoopedBy(Session S)
	{
		if(!snoops.contains(S))
			snoops.addElement(S);
	}
	public void stopBeingSnoopedBy(Session S)
	{
		while(snoops.contains(S))
			snoops.removeElement(S);
	}
	public boolean amBeingSnoopedBy(Session S)
	{
		if(S==null) return snoops.size()==0;
		return(snoops.contains(S));
	}

	public void setPreviousCmd(Vector cmds)
	{
		if(cmds==null) return;
		if(cmds.size()==0) return;
		if((cmds.size()>0)&&(((String)cmds.elementAt(0)).trim().startsWith("!")))
			return;

		previousCmd.removeAllElements();
		for(int i=0;i<cmds.size();i++)
			previousCmd.addElement(((String)cmds.elementAt(i)).toString());
	}

	public boolean afkFlag(){return afkFlag;}
	public void setAfkFlag(boolean truefalse)
	{
		if(afkFlag==truefalse) return;
		afkFlag=truefalse;
		if(afkFlag)
			println("\n\rYou are now listed as AFK.");
		else
			println("\n\rYou are no longer AFK.");
	}

	private void errorOut(Exception t)
	{
		Log.errOut("Session",t);
		Sessions.removeElement(this);
		killFlag=true;
	}

	public void out(char[] c)
	{
	    out.write(c);
	    out.flush();
	}

	public void onlyPrint(String msg){onlyPrint(msg,-1);}
	public synchronized void onlyPrint(String msg, int pageBreak)
	{
		if((out==null)||(msg==null)) return;
		try
		{
			if(snoops.size()>0)
				for(int s=0;s<snoops.size();s++)
					((Session)snoops.elementAt(s)).onlyPrint(msg,0);

			if(msg.endsWith("\n\r")
			&&(msg.equals(lastStr))
			&&(msg.length()>2)
			&&(msg.indexOf("\n")==(msg.length()-2)))
			{ spamStack++; return; }
			else
			if(spamStack>0)
			{
				if(spamStack>1)
					lastStr=lastStr.substring(0,lastStr.length()-2)+"("+spamStack+")"+lastStr.substring(lastStr.length()-2);
				out(lastStr.toCharArray());
			}

			spamStack=0;
			if(msg.startsWith("\n\r")&&(msg.length()>2))
				lastStr=msg.substring(2);
			else
				lastStr=msg;

			if(pageBreak<0)	pageBreak=CommonStrings.getIntVar(CommonStrings.SYSTEMI_PAGEBREAK);
			if(pageBreak>0)
			{
				int lines=0;
				for(int i=0;i<msg.length();i++)
				{
					if(msg.charAt(i)=='\n')
					{
						lines++;
						if(lines>=pageBreak)
						{
							lines=0;
							if((i<(msg.length()-1)&&(msg.charAt(i+1)=='\r')))
								i++;
							out(msg.substring(0,i).toCharArray());
							msg=msg.substring(i+1);
							out("<pause - enter>".toCharArray());
							try{
								blockingIn();
							}catch(Exception e){return;}
						}
					}
				}
			}
			out(msg.toCharArray());
		}
		catch(java.lang.NullPointerException e){}
	}

	public void rawPrint(String msg){rawPrint(msg,-1);}
	public void rawPrint(String msg, int pageBreak)
	{ if(msg==null)return;
	  onlyPrint((needPrompt?"":"\n\r")+msg,pageBreak);
	  needPrompt=true;
	}

	public void print(String msg)
	{ onlyPrint(CoffeeFilter.fullOutFilter(this,mob,mob,mob,null,msg,false),-1); }

	public void rawPrintln(String msg){rawPrintln(msg,-1);}
	public void rawPrintln(String msg, int pageBreak)
	{ if(msg==null)return; rawPrint(msg+"\n\r",pageBreak);}

	public void stdPrint(String msg)
	{ rawPrint(CoffeeFilter.fullOutFilter(this,mob,mob,mob,null,msg,false)); }

	public void print(Environmental src, Environmental trg, Environmental tol, String msg)
	{ onlyPrint((CoffeeFilter.fullOutFilter(this,mob,src,trg,tol,msg,false)),-1);}

	public void stdPrint(Environmental src, Environmental trg, Environmental tol, String msg)
	{ rawPrint(CoffeeFilter.fullOutFilter(this,mob,src,trg,trg,msg,false)); }

	public void println(String msg)
	{ if(msg==null)return; print(msg+"\n\r");}

	public void wraplessPrintln(String msg)
	{ if(msg==null)return;
	  onlyPrint(CoffeeFilter.fullOutFilter(this,mob,mob,mob,null,msg,true)+"\n\r",-1);
	  needPrompt=true;
	}

	public void wraplessPrint(String msg)
	{ onlyPrint(CoffeeFilter.fullOutFilter(this,mob,mob,mob,null,msg,true),-1);
	  needPrompt=true;
	}

	public void colorOnlyPrintln(String msg)
	{ colorOnlyPrint(msg,-1);}
	public void colorOnlyPrintln(String msg, int pageBreak)
	{ if(msg==null)return;
	  onlyPrint(CoffeeFilter.colorOnlyFilter(msg,this)+"\n\r",pageBreak);
	  needPrompt=true;
	}

	public void colorOnlyPrint(String msg)
	{ colorOnlyPrint(msg,-1);}
	public void colorOnlyPrint(String msg, int pageBreak)
	{ onlyPrint(CoffeeFilter.colorOnlyFilter(msg,this),pageBreak);
	  needPrompt=true;
	}

	public void stdPrintln(String msg)
	{ if(msg==null)return;
	  rawPrint(CoffeeFilter.fullOutFilter(this,mob,mob,mob,null,msg,false)+"\n\r");
	}

	public void println(Environmental src, Environmental trg, Environmental tol, String msg)
	{ if(msg==null)return;
	  onlyPrint(CoffeeFilter.fullOutFilter(this,mob,src,trg,tol,msg,false)+"\n\r",-1);
	}

	public void stdPrintln(Environmental src,Environmental trg, Environmental tol, String msg)
	{ if(msg==null)return;
	  rawPrint(CoffeeFilter.fullOutFilter(this,mob,src,trg,tol,msg,false)+"\n\r");
	}

	public void setPromptFlag(boolean truefalse)
	{
		needPrompt=truefalse;
	}

	public String prompt(String Message, String Default, long maxTime)
		throws IOException
	{
		String Msg=prompt(Message,maxTime).trim();
		if(Msg.equals(""))
            return Default;
        return Msg;
	}

	public String prompt(String Message, String Default)
		throws IOException
	{
		String Msg=prompt(Message,-1).trim();
		if(Msg.equals(""))
            return Default;
		return Msg;
	}

	public String prompt(String Message, long maxTime)
		throws IOException
	{
		print(Message);
		String input=blockingIn(maxTime);
		if(input==null) return "";
		if((input.length()>0)&&(input.charAt(input.length()-1)=='\\'))
			return input.substring(0,input.length()-1);
		return input;
	}

	public String prompt(String Message)
		throws IOException
	{
		print(Message);
		String input=blockingIn(-1);
		if(input==null) return "";
		if((input.length()>0)&&(input.charAt(input.length()-1)=='\\'))
			return input.substring(0,input.length()-1);
		return input;
	}

	public void cmdExit(MOB mob, Vector commands)
		throws Exception
	{
		if (confirm("\n\rQuit -- are you sure (y/N)?","N"))
		{
			killFlag=true;
		}
	}

	public int getColor(char c)
	{
		// warning do not nest!
		if (c == '?') return lastColor;
		if (c>255) return -1;
		return c;
	}

	public String[] clookup(){
		if(clookup==null)
			clookup=CMColor.standardColorLookups();

		if(mob()==null) return clookup;
		PlayerStats pstats=mob().playerStats();
		if((mob.soulMate()!=null)&&(mob.soulMate().playerStats()!=null))
		    pstats=mob.soulMate().playerStats();
		if(pstats==null) return clookup;

		if(!pstats.getColorStr().equals(lastColorStr))
		{
			if(pstats.getColorStr().length()==0)
				clookup=CMColor.standardColorLookups();
			else
			{
				String changes=pstats.getColorStr();
				lastColorStr=changes;
				clookup=(String[])CMColor.standardColorLookups().clone();
				int x=changes.indexOf("#");
				while(x>0)
				{
					String sub=changes.substring(0,x);
					changes=changes.substring(x+1);
					clookup[sub.charAt(0)]=CMColor.translateCMCodeToANSI(sub.substring(1));
					x=changes.indexOf("#");
				}
				for(int i=0;i<clookup.length;i++)
				{
					String s=clookup[i];
					if((s!=null)&&(s.startsWith("^"))&&(s.length()>1))
						clookup[i]=clookup[s.charAt(1)];
				}
			}
		}
		return clookup;
	}

	public String makeEscape(int c)
	{
	    switch(c)
	    {
	    	case '>':
	    	    if(currentColor>0)
					return clookup()[c]+clookup()[currentColor];
    	        return clookup()[c];
	    	case '<':
	    	case '&':
	    	case '"':
				return clookup()[c];
		    case '0': case '1': case '2': case '3': case '4': case '5':
		    case '6': case '7': case '8': case '9':
			{
				if((termID&TERM_MSP)==TERM_MSP)
					return CommonStrings.getVar(CommonStrings.SYSTEM_ESC0+(c-('0')));
				return "";
			}
		    default:
				break;
	    }
		if (((termID&TERM_ANSI)==TERM_ANSI) && (c != -1))
		{
			if ((c != currentColor)||(c=='^'))
			{
			    if(c!='.')
			    {
					lastColor = currentColor;
					currentColor = c;
			    }
				return clookup()[c];
			}
		}
		else
		{
			lastColor = currentColor;
			currentColor = 0;
		}
		return null;
	}

	private boolean appendInput(int c)
	{
		boolean rv = false;
		switch (c)
		{
			case 10:
			{
				c=-1;
				if(!lastWasCR)
				{
					lastWasLF = true;
					rv = true;
				}
				else
					lastWasLF = false;
				lastWasCR = false;
				break;
			}
			case 13:
			{
				c=-1;
				if(!lastWasLF)
				{
					lastWasCR = true;
					rv = true;
				}
				else
					lastWasCR = false;
				lastWasLF = false;
				break;
			}
			case 26:
			{
				lastWasCR = false;
				lastWasLF = false;
				// don't let them enter ANSI escape sequences...
				c = -1;
				break;
			}
			case 241:
			case 242:
			case 243:
			case 244:
			case 245:
			case 246:
			case 247:
			case 248:
			case 249:
			case 250:
			case 251:
			case 252:
			case 253:
			case 254:
			case 255:
			{
				lastWasCR = false;
				lastWasLF = false;
				// don't let them enter telnet codes, except IAC, which is handled...
				c = -1;
				break;
			}
			default:
			{
				lastWasCR = false;
				lastWasLF = false;
				break;
			}
		}

		if(c>0)
		{
			lastKeystroke=System.currentTimeMillis();
			input.append((char)c);
		}
		return rv;
	}

	public void handleIAC()
		throws IOException, InterruptedIOException
	{
		if((in==null)||(out==null))
			return;
		int c=in.read();
		switch(c)
		{
		case TELNET_WILL:
		{
		    //out.write(TELNET_IAC);
		    int last=in.read();
		    if(last==TELNET_MXP)
		    {
			    //out.write(TELNET_DO);
			    supports=supports|Session.TERM_MXP;
		    }
		    else
			    //out.write(TELNET_DONT);
		    //out.write(last);
		    out.flush();
		    break;
		}
		case TELNET_WONT:
		{
		    //out.write(TELNET_IAC);
		    //out.write(TELNET_DONT);
		    int last=in.read();
		    if(last==TELNET_MXP)
			    supports=Util.unsetb(supports,Session.TERM_MXP);
		    //out.write(last);
		    //out.flush();
		    break;
		}
		case TELNET_DO:
		{
		    out.write(TELNET_IAC);
		    int last=in.read();
		    if(last==TELNET_MXP)
		    {
			    out.write(TELNET_WILL);
		        supports=supports|Session.TERM_MXP;
		    }
		    else
			    out.write(TELNET_DONT);
		    out.write(last);
		    out.flush();
		    break;
		}
		case TELNET_DONT:
		{
		    out.write(TELNET_IAC);
		    out.write(TELNET_WONT);
		    int last=in.read();
		    if(last==TELNET_MXP)
			    supports=Util.unsetb(supports,Session.TERM_MXP);
		    out.write(last);
		    out.flush();
		    break;
		}
		default:
			return;
		}
	}

	public int nonBlockingIn(boolean appendInputFlag)
	throws IOException
	{
		try
		{
			int c=in.read();
			if(c<0)
				throw new IOException("reset by peer");
			else
			if(c==TELNET_IAC)
			    handleIAC();
			else
			if((appendInputFlag)&&(appendInput(c)))
				return 0;
		}
		catch(InterruptedIOException e)
		{
		    return -1;
		}
		return 1;
	}

	public String blockingIn(long maxTime)
		throws IOException
	{
		if((in==null)||(out==null)) return "";
		input=new StringBuffer("");
		long start=System.currentTimeMillis();
		try
		{
			suspendCommandLine=true;
			while((!killFlag)
			&&((maxTime<=0)||((System.currentTimeMillis()-start)<maxTime)))
			    if(nonBlockingIn(true)==0)
			        break;
			suspendCommandLine=false;
			if((maxTime>0)&&((System.currentTimeMillis()-start)>=maxTime))
				throw new java.io.InterruptedIOException("Timed Out.");

			StringBuffer inStr=CoffeeFilter.simpleInFilter(input,CMSecurity.isAllowed(mob,(mob!=null)?mob.location():null,"MXPTAGS"));
			input=new StringBuffer("");
			if(inStr==null) return null;
			return inStr.toString();
		}
		finally
		{
			suspendCommandLine=false;
		}
	}

	public String blockingIn()
		throws IOException
	{
		return blockingIn(-1);
	}

	public String readlineContinue()
		throws IOException, SocketException
	{

		if((in==null)||(out==null)) return "";
		int code=-1;
		while(!killFlag)
		{
		    code=nonBlockingIn(true);
		    if(code==1)
		        continue;
		    if(code==0)
		        break;
		    if(code==-1)
		        return null;
		}

		StringBuffer inStr=CoffeeFilter.simpleInFilter(input,CMSecurity.isAllowed(mob,(mob!=null)?mob.location():null,"MXPTAGS"));
		input=new StringBuffer("");
		if(inStr==null) return null;
		return inStr.toString();
	}

	public boolean confirm(String Message, String Default, long maxTime)
	throws IOException
	{
		String YN=choose(Message,"YN",Default,maxTime);
		if(YN.equals("Y"))
			return true;
		return false;
	}
	public boolean confirm(String Message, String Default)
	throws IOException
	{
		String YN=choose(Message,"YN",Default,-1);
		if(YN.equals("Y"))
			return true;
		return false;
	}

	public String choose(String Message, String Choices, String Default)
	throws IOException
	{ return choose(Message,Choices,Default,-1);}

	public String choose(String Message, String Choices, String Default, long maxTime)
	throws IOException
	{
		String YN="";
		while((YN.equals(""))||(Choices.indexOf(YN)<0)&&(!killFlag))
		{
			print(Message);
			YN=blockingIn(maxTime);
			if(YN==null){ return Default.toUpperCase(); }
			YN=YN.trim().toUpperCase();
			if(YN.equals("")){ return Default.toUpperCase(); }
			if(YN.length()>1) YN=YN.substring(0,1);
		}
		return YN;
	}

	public void logoff()
	{
		killFlag=true;
		this.interrupt();
		try{Thread.sleep(1000);}catch(Exception i){}
	}

	public void showPrompt()
	{
	    promptLastShown=System.currentTimeMillis();
		if(mob()==null) return;
		if(mob().playerStats()==null) return;
		StringBuffer buf=new StringBuffer("\n\r");
		String prompt=mob().playerStats().getPrompt();
		int c=0;
		if(Util.bset(getTermID(),Session.TERM_MXP))
		    buf.append("^<!EN Hp '"+mob().curState().getHitPoints()
					+"'^>^<!EN MaxHp '"+mob().maxState().getHitPoints()
					+"'^>^<!EN Mana '"+mob().curState().getMana()
					+"'^>^<!EN MaxMana '"+mob().maxState().getMana()
					+"'^>^<!EN Move '"+mob().curState().getMovement()
					+"'^>^<!EN MaxMove '"+mob().maxState().getMovement()
					+"'^>^<!EN Exp '"+mob().getExperience()
					+"'^>^<!EN ExpNeed '"+mob().getExpNeededLevel()
					+"'^>^<!EN MyImg '"+mob().image()+"'^>\n\r\n\r");
		while(c<prompt.length())
			if((prompt.charAt(c)=='%')&&(c<(prompt.length()-1)))
			{
				switch(prompt.charAt(++c))
				{
                case 'a': { buf.append(Factions.getRangePercent(Factions.AlignID(),mob.fetchFaction(Factions.AlignID()))+"%"); c++; break; }
                case 'A': { Faction.FactionRange FR=Factions.getRange(Factions.AlignID(),mob.fetchFaction(Factions.AlignID()));buf.append((FR!=null)?FR.Name:""+mob.fetchFaction(Factions.AlignID())); c++; break;}
                case 'B': { buf.append("\n\r"); c++; break;}
                case 'c': { buf.append(mob().inventorySize()); c++; break;}
                case 'C': { buf.append(mob().maxItems()); c++; break;}
                case 'd': {   MOB victim=mob().getVictim();
                              if((mob().isInCombat())&&(victim!=null))
                                  buf.append(""+mob().rangeToTarget());
                              c++; break; }
                case 'e': {   MOB victim=mob().getVictim();
                              if((mob().isInCombat())&&(victim!=null)&&(Sense.canBeSeenBy(victim,mob)))
                                  buf.append(victim.name());
                              c++; break; }
                case 'E': {   MOB victim=mob().getVictim();
                              if((mob().isInCombat())&&(victim!=null)&&(Sense.canBeSeenBy(victim,mob)))
                                  buf.append(victim.charStats().getMyRace().healthText(victim)+"\n\r");
                              c++; break; }
                case 'g': { buf.append((int)Math.round(Math.floor(BeanCounter.getTotalAbsoluteNativeValue(mob())/BeanCounter.getLowestDenomination(BeanCounter.getCurrency(mob()))))); c++; break;}
                case 'G': { buf.append(BeanCounter.nameCurrencyShort(mob(),BeanCounter.getTotalAbsoluteNativeValue(mob()))); c++; break;}
				case 'h': { buf.append("^<Hp^>"+mob().curState().getHitPoints()+"^</Hp^>"); c++; break;}
				case 'H': { buf.append("^<MaxHp^>"+mob().maxState().getHitPoints()+"^</MaxHp^>"); c++; break;}
                case 'I': {   if(Sense.isCloaked(mob()))
                                  buf.append("Cloaked");
                              else
                              if(!Sense.isSeen(mob()))
                                  buf.append("Undetectable");
                              else
                              if(Sense.isInvisible(mob())&&Sense.isHidden(mob()))
                                  buf.append("Hidden/Invisible");
                              else
                              if(Sense.isInvisible(mob()))
                                  buf.append("Invisible");
                              else
                              if(Sense.isHidden(mob()))
                                  buf.append("Hidden");
                              c++; break;}
                case 'K':
                case 'k': { MOB tank=mob();
                            if((tank.getVictim()!=null)
                            &&(tank.getVictim().getVictim()!=null)
                            &&(tank.getVictim().getVictim()!=mob()))
                                tank=tank.getVictim().getVictim();
                            if(((c+1)<prompt.length())&&(tank!=null))
                                switch(prompt.charAt(c+1))
                                {
                                    case 'h': { buf.append(tank.curState().getHitPoints()); c++; break;}
                                    case 'H': { buf.append(tank.maxState().getHitPoints()); c++; break;}
                                    case 'm': { buf.append(tank.curState().getMana()); c++; break;}
                                    case 'M': { buf.append(tank.maxState().getMana()); c++; break;}
                                    case 'v': { buf.append(tank.curState().getMovement()); c++; break;}
                                    case 'V': { buf.append(tank.maxState().getMovement()); c++; break;}
                                }
                            c++;
                            break;
                          }
				case 'm': { buf.append("^<Mana^>"+mob().curState().getMana()+"^</Mana^>"); c++; break;}
				case 'M': { buf.append("^<MaxMana^>"+mob().maxState().getMana()+"^</MaxMana^>"); c++; break;}
                case 'r': {   if(mob().location()!=null)
                              buf.append(mob().location().displayText());
                              c++; break; }
                case 'R': {   if((mob().location()!=null)&&CMSecurity.isAllowed(mob(),mob().location(),"SYSMSGS"))
                              buf.append(mob().location().roomID());
                              c++; break; }
				case 'v': { buf.append("^<Move^>"+mob().curState().getMovement()+"^</Move^>"); c++; break;}
				case 'V': { buf.append("^<MaxMove^>"+mob().maxState().getMovement()+"^</MaxMove^>"); c++; break;}
                case 'w': { buf.append(mob().envStats().weight()); c++; break;}
                case 'W': { buf.append(mob().maxCarry()); c++; break;}
				case 'x': { buf.append(mob().getExperience()); c++; break;}
				case 'X': {
							  if(mob().getExpNeededLevel()==Integer.MAX_VALUE)
								buf.append("N/A");
							  else
								buf.append(mob().getExpNeededLevel());
							  c++; break;
						  }
				case 'z': {      if((mob().location()!=null)&&(CMSecurity.isAllowed(mob(),mob().location(),"SYSMSGS")))
								  buf.append(mob().location().getArea().name());
							  c++; break; }
				case 't': {	  if(mob().location()!=null)
								  buf.append(Util.capitalizeAndLower(TimeClock.TOD_DESC[mob().location().getArea().getTimeObj().getTODCode()].toLowerCase()));
							  c++; break;
						  }
				case 'T': {	  if(mob().location()!=null)
								  buf.append(mob().location().getArea().getTimeObj().getTimeOfDay());
							  c++; break;
						  }
				case '@': {	  if(mob().location()!=null)
								  buf.append(mob().location().getArea().getClimateObj().weatherDescription(mob().location()));
							  c++; break;
						  }
				default:{ buf.append("%"+prompt.charAt(c)); c++; break;}
				}
			}
			else
				buf.append(prompt.charAt(c++));
		print("^<Prompt^>"+buf.toString()+"^</Prompt^>^.^N");
	}

	public void setTermID(int tid)
	{
		termID = tid;
	}
	public void initTermID(int mobbitmap)
	{
        termID=(Util.bset(mobbitmap,MOB.ATT_ANSI)?Session.TERM_ANSI:0)
        +(termID&Session.TERM_MXP)
        +(Util.bset(mobbitmap,MOB.ATT_SOUND)?Session.TERM_MSP:0);
	}

	private void closeSocks()
	{
		try
		{
			if(sock!=null)
			{
				status=Session.STATUS_LOGOUT6;
				if(out!=null)
					out.flush();
				status=Session.STATUS_LOGOUT7;
				sock.shutdownInput();
				status=Session.STATUS_LOGOUT8;
				sock.shutdownOutput();
				status=Session.STATUS_LOGOUT9;
				if(out!=null)
					out.close();
				status=Session.STATUS_LOGOUT10;
				sock.close();
				status=Session.STATUS_LOGOUT11;
			}
			in=null;
			out=null;
			sock=null;
		}
		catch(IOException e)
		{
		}
	}

	public int getTermID()
	{
		return termID;
	}

	public String getAddress()
	{
		try
		{
			return sock.getInetAddress().getHostAddress();
		}
		catch (Exception e)
		{
			return "Unknown (Excpt "+e.getMessage() + ")";
		}
	}

	public int getStatus(){return status;}

	public void run()
	{
		status=Session.STATUS_LOGIN;
		try
		{
			long tries=0;
			while((!killFlag)&&((++tries)<5))
			{
				MOB newMob=CMClass.getMOB("StdMOB");
				newMob.setSession(this);
				mob=newMob;
				status=Session.STATUS_LOGIN;
				Command C=CMClass.getCommand("FrontLogin");
				String input=null;
                Vector tryV=Util.makeVector(""+tries);
				if((C!=null)&&(C.execute(mob,tryV)))
				{
					status=Session.STATUS_LOGIN2;
					if((!killFlag)&&(mob!=null))
                    {
						Log.sysOut("Session","login: "+mob.Name());
                        if((tryV.size()<1)||(!(tryV.firstElement() instanceof String))||(!((String)tryV.firstElement()).equalsIgnoreCase("SWAPPED")))
                            if(!CMMap.sendGlobalMessage(mob,CMMsg.TYP_LOGIN,new FullMsg(mob,null,CMMsg.MSG_LOGIN,null)))
                                killFlag=true;
                    }
					needPrompt=true;
					Vector CMDS=null;
					while((!killFlag)&&(mob!=null))
					{
						status=Session.STATUS_OK;
						lastLoopTop=System.currentTimeMillis();
						waiting=true;
						if(suspendCommandLine)
						{
							input=null;
							try{Thread.sleep(100);}catch(Exception e){}
						}
						else
							input=readlineContinue();
						if(input!=null)
						{
							lastKeystroke=System.currentTimeMillis();
							setAfkFlag(false);
							CMDS=Util.parse(input);
							if(CMDS.size()>0)
							{
								waiting=false;
								setPreviousCmd(CMDS);
								milliTotal+=(lastStop-lastStart);

								if(snoops.size()>0)
									for(int s=0;s<snoops.size();s++)
										((Session)snoops.elementAt(s)).rawPrintln(input);

								lastStart=System.currentTimeMillis();
								ExtendedParser.queExtendedCommands(mob, CMDS);
								lastStop=System.currentTimeMillis();
							}
							needPrompt=true;
						}
						if(mob==null) break;
                        while((dequeCounter>0)&&(mob!=null))
                        {
                            mob.dequeCommand();
                            dequeCounter--;
                        }

						//if((spamStack>0)&&((lastOutput-System.currentTimeMillis())>100))
						//	onlyPrint("",0);

						if(!afkFlag())
						{
							if(getIdleMillis()>=600000)
								setAfkFlag(true);
						}
						else
						if((getIdleMillis()>=10800000)
						&&(mob()!=null)
						&&((System.currentTimeMillis()-lastBlahCheck)>=60000))
						{
							lastBlahCheck=System.currentTimeMillis();
							if((!Sense.isSleeping(mob))
							&&(mob().fetchEffect("Disease_Blahs")==null))
							{
								Ability A=CMClass.getAbility("Disease_Blahs");
								if(A!=null) A.invoke(mob,mob,true,0);
							}
							else
							if((Sense.isSleeping(mob))
							&&(mob().fetchEffect("Disease_Narcolepsy")==null))
							{
								Ability A=CMClass.getAbility("Disease_Narcolepsy");
								if(A!=null) A.invoke(mob,mob,true,0);
							}
						}
						if((needPrompt)&&(waiting))
						{
						    if((mob!=null)&&(mob.isInCombat()))
						    {
						        if(((System.currentTimeMillis()-promptLastShown)>=MudHost.TICK_TIME)
						        ||(input!=null))
						        {
									showPrompt();
									needPrompt=false;
						        }
						    }
						    else
						    {
								showPrompt();
								needPrompt=false;
						    }
						}
					}
					status=Session.STATUS_LOGOUT2;
				}
				else
				{
					mob=null;
					newMob.setSession(null);
				}
				status=Session.STATUS_LOGOUT;
			}
			status=Session.STATUS_LOGOUT3;
		}
		catch(SocketException e)
		{
			if(!Log.isMaskedErrMsg(e.getMessage()))
				errorOut(e);
		}
		catch(Exception t)
		{
			if(!Log.isMaskedErrMsg(t.getMessage()))
				errorOut(t);
		}
		status=Session.STATUS_LOGOUT3;

		if(mob!=null)
		{
			String name=mob.Name();
			if(name.trim().length()==0) name="Unknown";
            if((mob.isInCombat())&&(mob.location()!=null))
            {
                CommonMsgs.flee(mob,"NOWHERE");
                mob.makePeace();
            }
			CommonMsgs.channel("WIZINFO","",name+" has logged out.",true);
			// the player quit message!
            loginLogoutThread LT=new loginLogoutThread(mob,CMMsg.MSG_QUIT);
            LT.start();
			if(mob.playerStats()!=null)
				mob.playerStats().setLastDateTime(System.currentTimeMillis());
			Log.sysOut("Session","logout: "+name);
			mob.removeFromGame();
			mob.setSession(null);
			mob=null;
		}

		status=Session.STATUS_LOGOUT4;
		killFlag=true;
		waiting=false;
		needPrompt=false;
		snoops.clear();

		closeSocks();


		status=Session.STATUS_LOGOUT5;
		Sessions.removeElement(this);

		//finally
		//{
		//}
		status=Session.STATUS_LOGOUTFINAL;
	}
    private class loginLogoutThread extends Thread implements Tickable
    {
        public String name(){return (theMOB==null)?"Dead LLThread":"LLThread for "+theMOB.Name();}
        public boolean tick(Tickable ticking, int tickID){return false;}
        public String ID(){return name();}
        public long getTickStatus(){return 0;}
        private MOB theMOB=null;
        private int msgCode=-1;
        private loginLogoutThread(){}
        public loginLogoutThread(MOB mob, int msg)
        {
            theMOB=mob;
            msgCode=msg;
        }

        public void run()
        {
            FullMsg msg=new FullMsg(theMOB,null,msgCode,null);
            Room R=null;
            try{
                for(Enumeration e=CMMap.rooms();e.hasMoreElements();)
                {
                    R=(Room)e.nextElement();
                    if(theMOB.location()!=null)
                    {
                        R.send(theMOB,msg);
                    }
                }
            }catch(Exception e){}
            theMOB=null;
        }
    }
}