/
com/planet_ink/coffee_mud/Abilities/Common/
com/planet_ink/coffee_mud/Abilities/Diseases/
com/planet_ink/coffee_mud/Abilities/Druid/
com/planet_ink/coffee_mud/Abilities/Fighter/
com/planet_ink/coffee_mud/Abilities/Languages/
com/planet_ink/coffee_mud/Abilities/Misc/
com/planet_ink/coffee_mud/Abilities/Prayers/
com/planet_ink/coffee_mud/Abilities/Properties/
com/planet_ink/coffee_mud/Abilities/Skills/
com/planet_ink/coffee_mud/Abilities/Songs/
com/planet_ink/coffee_mud/Abilities/Spells/
com/planet_ink/coffee_mud/Abilities/Thief/
com/planet_ink/coffee_mud/Abilities/Traps/
com/planet_ink/coffee_mud/Behaviors/
com/planet_ink/coffee_mud/CharClasses/interfaces/
com/planet_ink/coffee_mud/Commands/
com/planet_ink/coffee_mud/Commands/interfaces/
com/planet_ink/coffee_mud/Common/
com/planet_ink/coffee_mud/Common/interfaces/
com/planet_ink/coffee_mud/Exits/interfaces/
com/planet_ink/coffee_mud/Items/Armor/
com/planet_ink/coffee_mud/Items/Basic/
com/planet_ink/coffee_mud/Items/CompTech/
com/planet_ink/coffee_mud/Items/MiscMagic/
com/planet_ink/coffee_mud/Items/Weapons/
com/planet_ink/coffee_mud/Items/interfaces/
com/planet_ink/coffee_mud/Libraries/
com/planet_ink/coffee_mud/Libraries/interfaces/
com/planet_ink/coffee_mud/Locales/
com/planet_ink/coffee_mud/MOBS/
com/planet_ink/coffee_mud/Races/
com/planet_ink/coffee_mud/Races/interfaces/
com/planet_ink/coffee_mud/WebMacros/
com/planet_ink/coffee_mud/WebMacros/interfaces/
com/planet_ink/coffee_mud/core/
com/planet_ink/coffee_mud/core/collections/
com/planet_ink/coffee_mud/core/interfaces/
com/planet_ink/coffee_mud/core/intermud/
com/planet_ink/coffee_mud/core/intermud/i3/
com/planet_ink/coffee_web/server/
com/planet_ink/siplet/applet/
lib/
resources/factions/
resources/fakedb/
resources/progs/autoplayer/
resources/quests/holidays/
web/
web/admin.templates/
web/admin/grinder/
web/admin/images/
web/clan.templates/
web/pub.templates/
web/pub/images/mxp/
web/pub/sounds/
web/pub/textedit/
package com.planet_ink.coffee_mud.core.intermud.i3.server;
import com.planet_ink.coffee_mud.core.intermud.imc2.*;
import com.planet_ink.coffee_mud.core.intermud.i3.packets.*;
import com.planet_ink.coffee_mud.core.intermud.i3.persist.*;
import com.planet_ink.coffee_mud.core.intermud.i3.server.*;
import com.planet_ink.coffee_mud.core.intermud.i3.net.*;
import com.planet_ink.coffee_mud.core.intermud.*;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.CMSecurity.DisFlag;
import com.planet_ink.coffee_mud.core.collections.*;
import com.planet_ink.coffee_mud.Abilities.interfaces.*;
import com.planet_ink.coffee_mud.Areas.interfaces.*;
import com.planet_ink.coffee_mud.Behaviors.interfaces.*;
import com.planet_ink.coffee_mud.CharClasses.interfaces.*;
import com.planet_ink.coffee_mud.Commands.interfaces.*;
import com.planet_ink.coffee_mud.Common.interfaces.*;
import com.planet_ink.coffee_mud.Exits.interfaces.*;
import com.planet_ink.coffee_mud.Items.interfaces.*;
import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_mud.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;

/**
 * The Server class uses exactly one thread ServerThread object
 * during the course of program execution.  This thread loops
 * until the Server class tells it to shut down.  The loop is
 * executed in the thread's run() method.
 * @author George Reese (borg@imaginary.com), Bo Zimmerman
 * @version 1.1
 * @see com.planet_ink.coffee_mud.core.intermud.i3.server.I3Server
 *
 * Modified in 2013 to cut down on thread use
 */
@SuppressWarnings({"unchecked","rawtypes"})
public class ServerThread implements Tickable
{
	private java.util.Date  boot_time=null;
	private int 			count  = 1;
	private final String  		mud_name;
	private final int 			port;
	private boolean 		running;
	private final ImudServices	intermuds;
	private ListenThread 	listen_thread=null;
	private volatile int 	tickStatus=Tickable.STATUS_NOT;
	private Map<String,ServerObject> objects;
	private Map<String,ServerUser>   interactives;

	protected ServerThread(String mname,
						   int mport,
						   ImudServices imud)
						   {
		mud_name = mname;
		port = mport;
		intermuds=imud;
	}

	@Override public String ID() { return "I3ServerThread"; }
	@Override public CMObject newInstance() { return this; }
	@Override public CMObject copyOf() { return this; }
	@Override public void initializeClass() { }
	@Override public int compareTo(CMObject o) { return (o==this)?0:1; }
	@Override public String name() { return "I3UserThread"+Thread.currentThread().getThreadGroup().getName().charAt(0); }
	@Override public int getTickStatus() { return tickStatus; }

	protected synchronized ServerObject copyObject(String str) throws ObjectLoadException {
		ServerObject ob;

		try
		{
			ob = (ServerObject)Class.forName(str).newInstance();
			count++;
			str = str + "#" + count;
			ob.setObjectId(str);
			objects.put(str, ob);
		}
		catch( final Exception e )
		{
			throw new ObjectLoadException("Failed to load object: " + e.getMessage());
		}
		return ob;
	}

	protected synchronized ServerObject findObject(String str) throws ObjectLoadException {
		ServerObject ob;

		if( objects.containsKey(str) )
		{
			ob = objects.get(str);
			if( ob.getDestructed() )
			{
				ob = null;
			}
		}
		else
		{
			ob = null;
		}
		if( ob == null )
		{
			try
			{
				ob = (ServerObject)Class.forName(str).newInstance();
				ob.setObjectId(str);
				objects.put(str, ob);
			}
			catch( final Exception e )
			{
				throw new ObjectLoadException("Failed to load object: " + e.getMessage());
			}
		}
		return ob;
	}

	protected synchronized void removeObject(ServerObject ob)
	{
		final String id = ob.getObjectId();

		if( !objects.containsKey(id) )
		{
			return;
		}
		objects.remove(id);
		if( interactives.containsKey(id) )
		{
			interactives.remove(id);
		}
	}

	/**
	 * While the mud is running, this method repeats the following
	 * steps over and over:
	 * 
	 * Check for pending user input and trigger user commands
	 * Check for pending object events and execute them
	 * Check for incoming user connections and create an
	 *  	interactive object for each.
	 * 
	 */
	public void start()
	{
		if( boot_time != null )
		{
			Log.errOut("I3Server","Illegal attempt to invoke run().");
			return;
		}

		try
		{
			listen_thread = new ListenThread(port);
		}
		catch( final java.io.IOException e )
		{
			Log.errOut("I3Server",e);
			return;
		}

		try
		{
			Intermud.setup(intermuds,
						   (PersistentPeer)Class.forName("com.planet_ink.coffee_mud.core.intermud.i3.IMudPeer").newInstance());
		}
		catch( final Exception e )
		{
			Log.errOut("I3Server",e);
			return;
		}

		boot_time = new java.util.Date();
		Log.sysOut("I3Server", "InterMud3 Server started on port "+port);

		synchronized( this )
		{
			objects = new Hashtable(1000, 100);
			interactives = new Hashtable(50, 20);
		}

		running = true;
		CMLib.threads().deleteTick(this, Tickable.TICKID_SUPPORT);
		CMLib.threads().startTickDown(this, Tickable.TICKID_SUPPORT, 250, 1);
	}

	@Override public boolean tick(Tickable ticking, int tickID)
	{
		if(CMSecurity.isDisabled(DisFlag.I3))
			return running;

		tickStatus=Tickable.STATUS_ALIVE;
		ServerObject[] things;
		ServerUser[] users;
		synchronized( this )
		{
			things = getObjects();
			users = getInteractives();
		}
		{// Process all input
			int i;

			for(i=0; i<users.length; i++)
			{
				final ServerUser interactive = users[i];


				if( interactive.getDestructed() )
				{
					continue;
				}
				try
				{
					interactive.processInput();
				}
				catch( final Exception e )
				{
					Log.errOut("IMServerThread",e);
				}
			}
		}
		{// Check for pending object events
			int i;

			for(i=0; i<things.length; i++)
			{
				final ServerObject thing = things[i];

				if( !thing.getDestructed() )
				{
					try
					{
						thing.processEvent();
					}
					catch( final Exception e )
					{
						Log.errOut("IMServerThread",e);
					}
				}
			}
		}
		{// Get new connections
			int i;

			for(i=0; i<5; i++)
			{
				java.net.Socket s;
				ServerUser new_user;

				if(listen_thread!=null)
					s = listen_thread.nextSocket();
				else
					s=null;
				if( s == null )
				{
					break;
				}
				try
				{
					new_user = (ServerUser)copyObject("com.planet_ink.coffee_mud.core.intermud.i3.IMudUser");
				}
				catch( final ObjectLoadException e )
				{
					continue;
				}
				try
				{
					new_user.setSocket(s);
					synchronized( this )
					{
						interactives.put(new_user.getObjectId(), new_user);
						new_user.connect();
					}
				}
				catch( final java.io.IOException e )
				{
					new_user.destruct();
				}
			}
		}
		tickStatus=Tickable.STATUS_NOT;
		return running;
	}

	protected Date getBootTime()
	{
		return boot_time;
	}

	protected synchronized ServerUser[] getInteractives()
	{
		final ServerUser[] tmp = new ServerUser[interactives.size()];
		int i = 0;

		for(final ServerUser U : interactives.values())
		{
			tmp[i++] = U;
		}
		return tmp;
	}

	protected String getMudName()
	{
		return mud_name;
	}

	protected int getPort()
	{
		return port;
	}

	public void shutdown()
	{
		running=false;
		Intermud.shutdown();
		if(listen_thread!=null)
		{
			listen_thread.close();
			CMLib.killThread(listen_thread,500,1);
			listen_thread=null;
		}
		boot_time = null;
		CMLib.threads().deleteTick(this, Tickable.TICKID_SUPPORT);
	}

	protected synchronized ServerObject[] getObjects()
	{
		final ServerObject[] tmp = new ServerObject[objects.size()];
		int i = 0;
		for(final ServerObject O : objects.values())
		{
			tmp[i++] = O;
		}
		return tmp;
	}
}