<NME> ServiceEngine.java
<BEF> package com.planet_ink.coffee_mud.core.threads;
import com.planet_ink.coffee_mud.core.database.DBInterface;
import com.planet_ink.coffee_mud.core.interfaces.*;
import com.planet_ink.coffee_mud.core.*;
import com.planet_ink.coffee_mud.core.CMLib.Library;
import com.planet_ink.coffee_mud.core.CMProps.Str;
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.Locales.interfaces.*;
import com.planet_ink.coffee_mud.MOBS.interfaces.*;
import com.planet_ink.coffee_mud.Races.interfaces.*;

import java.io.File;
import java.io.PrintStream;
import java.util.*;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.planet_ink.coffee_mud.Libraries.interfaces.*;
import com.planet_ink.coffee_web.interfaces.HTTPRequest;

/*
   Copyright 2001-2022 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 ServiceEngine implements ThreadEngine
{
	public static final  long STATUS_ALLMISCTICKS=Tickable.STATUS_MISC|Tickable.STATUS_MISC2|Tickable.STATUS_MISC3|Tickable.STATUS_MISC4|Tickable.STATUS_MISC5|Tickable.STATUS_MISC6;
	private static final long SHORT_TICK_TIMEOUT = (5*TimeManager.MILI_MINUTE);
	private static final long LONG_TICK_TIMEOUT  = (120*TimeManager.MILI_MINUTE);

	private Thread  				drivingThread		= null;
	private TickClient 				supportClient		= null;
	protected List<TickableGroup>	allTicks			= new SLinkedList<TickableGroup>();
	private boolean 				isSuspended			= false;
	private int 					max_objs_per_thread	= 0;
	private CMThreadPoolExecutor[]	threadPools			= new CMThreadPoolExecutor[256];
	private volatile long			globalTickID		= 0;
	private final long 				globalStartTime		= System.currentTimeMillis();
	private volatile CMRunnable[]	unsuspendedRunnables= null;
	private volatile long			nextWakeAtTime		= 0;
	private final List<CMRunnable>	schedTicks			= new LinkedList<CMRunnable>();

	@Override
	public String ID()
	{
		return "ServiceEngine";
	}

	@Override
	public String name()
	{
		return ID();
	}

	@Override
	public CMObject newInstance()
	{
		try
		{
			return getClass().newInstance();
		}
		catch (final Exception e)
		{
			return new ServiceEngine();
		}
	}

	@Override
	public String L(final String str, final String... xs)
	{
		return CMLib.lang().fullSessionTranslation(str, xs);
	}

	@Override
	public void initializeClass()
	{
	}

	@Override
	public CMObject copyOf()
	{
		try
		{
			return (CMObject) this.clone();
		}
		catch (final Exception e)
		{
			return newInstance();
		}
	}

	@Override
	public int compareTo(final CMObject o)
	{
		return CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o));
	}

	@Override
	public void propertiesLoaded()
	{
	}

	@Override
	public TickClient getServiceClient()
	{
		return supportClient;
	}

	public ServiceEngine()
	{
		initializeClass();
	}

	protected CMThreadPoolExecutor getPoolExecutor(final char threadGroupNum)
	{
		final CMThreadPoolExecutor pool = threadPools[threadGroupNum];
		if(pool != null)
			return pool;
		final int minThreads = CMProps.getIntVar(CMProps.Int.MINWORKERTHREADS);
		int maxThreads = CMProps.getIntVar(CMProps.Int.MAXWORKERTHREADS);
		if(maxThreads<=0)
			maxThreads=Integer.MAX_VALUE;
		final String sessionThreadGroupName="Worker"+threadGroupNum;
		threadPools[threadGroupNum] = new CMThreadPoolExecutor(sessionThreadGroupName,minThreads, maxThreads, CMProps.getTickMillis()*2, TimeUnit.MILLISECONDS, (LONG_TICK_TIMEOUT/60000), 1024);
		threadPools[threadGroupNum].setThreadFactory(new CMThreadFactory(sessionThreadGroupName));
		return threadPools[threadGroupNum];
	}

	protected CMThreadPoolExecutor getPoolExecutor(final String threadGroupName)
	{
		if(threadGroupName == null)
			return getPoolExecutor(Thread.currentThread().getThreadGroup().getName().charAt(0));
		else
			return getPoolExecutor(threadGroupName.charAt(0));
	}

	@Override
	public Iterator<TickableGroup> tickGroups()
	{
		return allTicks.iterator();
	}

	@Override
	public Runnable findRunnableByThread(final Thread thread)
	{
		if((thread==null)||(threadPools==null))
			return null;
		final Runnable possR=(thread instanceof CMFactoryThread)?((CMFactoryThread)thread).getRunnable():null;
		for (final CMThreadPoolExecutor threadPool : threadPools)
		{
			final CMThreadPoolExecutor executor=threadPool;
			if(executor==null)
				continue;
			if(possR!=null)
			{
				synchronized(executor.active)
				{
					if(executor.active.get(possR)==thread)
						return possR;
				}
			}
			if((executor.getThreadFactory() instanceof CMThreadFactory)
			&&(!((CMThreadFactory)executor.getThreadFactory()).getThreads().contains(thread)))
				continue;
			synchronized(executor.active)
			{
				if(!executor.active.containsValue(thread))
					continue;
				for(final Map.Entry<Runnable, Thread> e : executor.active.entrySet())
				{
					if(e.getValue()==thread)
						return e.getKey();
				}
			}
		}
		return null;
	}

	@Override
    public void scheduleRunnable(final Runnable R, final long ellapsedMs)
	{
		try
		{
			synchronized(this.schedTicks)
			{
				final long myNextTime = System.currentTimeMillis() + ellapsedMs;
				final char currentThreadId = Thread.currentThread().getThreadGroup().getName().charAt(0);
				schedTicks.add(new CMRunnable()
				{
					@Override
					public void run()
					{
						try
						{
							R.run();
						}
						catch(final Throwable t)
						{
							Log.errOut(t);
						}
					}

					@Override
					public long activeTimeMillis()
					{
						return System.currentTimeMillis() - getStartTime();
					}

					@Override
					public long getStartTime()
					{
						return myNextTime;
					}

					@Override
					public int getGroupID()
					{
						return currentThreadId;
					}
				});
				if((this.nextWakeAtTime == 0) || (myNextTime < this.nextWakeAtTime))
				{
					this.nextWakeAtTime = myNextTime;
					if(drivingThread!=null)
						drivingThread.interrupt();
				}
			}
		}
		catch(final Exception e)
		{
			Log.errOut("ServiceEngine","ExecRun: "+e.getMessage());
			Log.debugOut("ServiceEngine",e);
		}
	}

	@Override
	public void executeRunnable(final String threadGroupName, final Runnable R)
	{
		try
		{
			getPoolExecutor(threadGroupName).execute(R);
		}
		catch(final Exception e)
		{
			Log.errOut("ServiceEngine","ExecRun: "+e.getMessage());
			Log.debugOut("ServiceEngine",e);
		}
	}

	@Override
	public void executeRunnable(final char threadGroupId, final Runnable R)
	{
		try
		{
			getPoolExecutor(threadGroupId).execute(R);
		}
		catch(final Exception e)
		{
			Log.errOut("ServiceEngine","ExecRun: "+e.getMessage());
			Log.debugOut("ServiceEngine",e);
		}
	}

	@Override
	public void executeRunnable(final Runnable R)
	{
		try
		{
			getPoolExecutor(null).execute(R);
		}
		catch(final Exception e)
		{
			Log.errOut("ServiceEngine","ExecRun: "+e.getMessage());
			Log.debugOut("ServiceEngine",e);
		}
	}

	public int getMaxObjectsPerThread()
	{
		if(max_objs_per_thread>0)
			return max_objs_per_thread;
		max_objs_per_thread = CMProps.getIntVar(CMProps.Int.OBJSPERTHREAD);
		if(max_objs_per_thread>0)
			return max_objs_per_thread;
		max_objs_per_thread=0;
		return 128;
	}

	protected long getTicksEllapsedSinceStartup()
	{
		return globalTickID;
	}

	protected void delTickGroup(final TickableGroup tock)
	{
		allTicks.remove(tock);
		if(drivingThread!=null)
			drivingThread.interrupt();
	}
	}

	@Override
	public TickClient startTickDown(final Tickable E, final int tickID, final long TICK_TIME, final int numTicks)
	{
		return startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,TICK_TIME,numTicks);
	}

	public synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTime, final int numTicks)
	{
		TickableGroup tock=null;
		if(group==null)
		return startTickDown(E,tickID,CMProps.getTickMillis(),numTicks);
	}

	@Override
	public TickClient startTickDown(final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)
	{
		return startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,tickTimeMs,numTicks);
	}
				return null;
			}
			if((tock==null)
			&&(almostTock.getTickInterval()==tickTime)
			&&(!almostTock.isSolitaryTicker())
			&&(almostTock.numTickers()<getMaxObjectsPerThread()))
			{
		for(final TickableGroup almostTock : allTicks)
		{
			if(almostTock.contains(E,tickID))
			{
				return almostTock.getTickInterval();
			}
		final boolean isSolitary = ((tickID&Tickable.TICKID_SOLITARYMASK)==Tickable.TICKID_SOLITARYMASK);
		if((tock==null)||isSolitary)
		{
			tock=new StdTickGroup(this, tickTime, Thread.currentThread().getThreadGroup().getName(), isSolitary);
			addTickGroup(tock);
		}

		if(group==null)
			group=Thread.currentThread().getThreadGroup();
		final char threadGroupNum=group.getName().charAt(0);
		for(final TickableGroup almostTock : allTicks)
		{
			if(almostTock.contains(E,tickID))
			{
				for(final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);set.hasNext();)
					set.next().setSuspended(false);
				return null;
			}
			if((tock==null)
			&&(almostTock.getTickInterval()==tickTimeMs)
			&&(!almostTock.isSolitaryTicker())
			&&(almostTock.numTickers()<getMaxObjectsPerThread()))
			{
				final String name = almostTock.getThreadGroupName();
				if((name!=null)
				&&(name.charAt(0)==threadGroupNum))
					tock=almostTock;
			}
		}
		final boolean isSolitary = ((tickID&Tickable.TICKID_SOLITARYMASK)==Tickable.TICKID_SOLITARYMASK);
		if((tock==null)||isSolitary)
		{
			tock=new StdTickGroup(this, tickTimeMs, group.getName(), isSolitary);
			addTickGroup(tock);
		}

		final TickClient newC=new StdTickClient(E,numTicks,tickID);
		tock.addTicker(newC);
		return newC;
	}

	@Override
	public synchronized boolean deleteTick(final Tickable E, final int tickID)
	{
		boolean foundOne=false;
		for(final TickableGroup almostTock : allTicks)
		{
			final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);
			foundOne = foundOne || set.hasNext();
			for(;set.hasNext();)
				almostTock.delTicker(set.next());
		}
		return foundOne;
	}

	@Override
	public synchronized boolean setTickPending(final Tickable E, final int tickID)
	{
		boolean foundOne=false;
		for(final TickableGroup almostTock : allTicks)
		{
			final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);
			foundOne = foundOne || set.hasNext();
			for(;set.hasNext();)
			{
				final TickClient C = set.next();
				C.setCurrentTickDownPending();
			}
		}
		return foundOne;
	}

	@Override
	public long getTimeMsToNextTick(final Tickable E, final int tickID)
	{
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			final TickableGroup almostTock=e.next();
			final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);
			if(set.hasNext())
			{
				final TickClient client = set.next();
				return client.getTimeMSToNextTick();
			}
		}
		return -1;
	}

	@Override
	public boolean isTicking(final Tickable E, final int tickID)
	{
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			final TickableGroup almostTock=e.next();
			final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);
			if(set.hasNext())
				return true;
		}
		return false;
	}

	@Override
	public boolean isAllSuspended()
	{
		return isSuspended;
	}

	@Override
	public void suspendAll(final CMRunnable[] exceptRs)
	{
		unsuspendedRunnables = exceptRs;
		isSuspended = true;
	}

	@Override
	public void resumeAll()
	{
		unsuspendedRunnables = null;
		isSuspended = false;
	}

	@Override
	public void suspendTicking(final Tickable E, final int tickID)
	{
		suspendResumeTicking(E, tickID, true);
	}

	@Override
	public void resumeTicking(final Tickable E, final int tickID)
	{
		suspendResumeTicking(E, tickID, false);
	}

	protected boolean suspendResumeTicking(final Tickable E, final int tickID, final boolean suspend)
	{
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			final TickableGroup almostTock=e.next();
			for(final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);set.hasNext();)
				set.next().setSuspended(suspend);
		}
		return false;
	}

	@Override
	public boolean isSuspended(final Tickable E, final int tickID)
	{
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			final TickableGroup almostTock=e.next();
			final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);
			if(set.hasNext() && set.next().isSuspended())
				return true;
		}
		return false;
	}

	@Override
	public String getSystemReport(final String itemCode)
	{
		final String cd=itemCode.toLowerCase();
		if(cd.startsWith("topmob")||cd.startsWith("totalmob"))
		{
			long totalMOBMillis=0;
			long totalMOBTicks=0;
			long topMOBMillis=0;
			long topMOBTicks=0;
			MOB topMOBClient=null;
			for(final Session S : CMLib.sessions().localOnlineIterable())
			{
				totalMOBMillis+=S.getTotalMillis();
				totalMOBTicks+=S.getTotalTicks();
				if(S.getTotalMillis()>topMOBMillis)
				{
					topMOBMillis=S.getTotalMillis();
					topMOBTicks=S.getTotalTicks();
					topMOBClient=S.mob();
				}
			}

			if(itemCode.equalsIgnoreCase("totalMOBMillis"))
				return ""+totalMOBMillis;
			else
			if(itemCode.equalsIgnoreCase("totalMOBMillisTime"))
				return CMLib.english().stringifyElapsedTimeOrTicks(totalMOBMillis,0);
			else
			if(itemCode.equalsIgnoreCase("totalMOBMillisTimePlusAverage"))
				return CMLib.english().stringifyElapsedTimeOrTicks(totalMOBMillis,totalMOBTicks);
			else
			if(itemCode.equalsIgnoreCase("totalMOBTicks"))
				return ""+totalMOBTicks;
			else
			if(itemCode.equalsIgnoreCase("topMOBMillis"))
				return ""+topMOBMillis;
			else
			if(itemCode.equalsIgnoreCase("topMOBMillisTime"))
				return CMLib.english().stringifyElapsedTimeOrTicks(topMOBMillis,0);
			else
			if(itemCode.equalsIgnoreCase("topMOBMillisTimePlusAverage"))
				return CMLib.english().stringifyElapsedTimeOrTicks(topMOBMillis,topMOBTicks);
			else
			if(itemCode.equalsIgnoreCase("topMOBTicks"))
				return ""+topMOBTicks;
			else
			if(itemCode.equalsIgnoreCase("topMOBClient"))
			{
				if(topMOBClient!=null)
					return topMOBClient.Name();
				return "";
			}
			return "";
		}
		else
		if(itemCode.toLowerCase().startsWith("tickerproblems"))
		{
			final int x=itemCode.indexOf('-');
			int total=10;
			if(x>0)
				total=CMath.s_int(itemCode.substring(x+1));
			final Vector<Triad<Long,Integer,Integer>> list=new Vector<Triad<Long,Integer,Integer>>();
			int group=0;
			for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
			{
				final TickableGroup almostTock=e.next();
				int tick=0;
				for(final Iterator<TickClient> et=almostTock.tickers();et.hasNext();)
				{
					final TickClient C=et.next();
					if(C.getTickTotal()==0)
						continue;
					final Long avg=Long.valueOf(C.getMilliTotal()/C.getTickTotal());
					int i=0;
					for(;i<list.size();i++)
					{
						final Triad<Long,Integer,Integer> t=list.get(i);
						if(avg.longValue()>=t.first.longValue())
						{
							list.add(i,new Triad<Long,Integer,Integer>(avg,Integer.valueOf(group),Integer.valueOf(tick)));
							break;
						}
					}
					if((list.size()==0)||((i>=list.size())&&(list.size()<total)))
						list.add(new Triad<Long,Integer,Integer>(avg,Integer.valueOf(group),Integer.valueOf(tick)));
					while(list.size()>total)
						list.remove(list.size()-1);
					tick++;
				}
				group++;
			}
			if(list.size()==0)
				return "";
			final StringBuilder str=new StringBuilder("");
			for(final Triad<Long,Integer,Integer> t : list)
				str.append(';').append(t.second).append(',').append(t.third);
			return str.toString().substring(1);
		}
		else
		if(itemCode.toLowerCase().startsWith("tickerprob2"))
		{
			final int x=itemCode.indexOf('-');
			int total=10;
			if(x>0)
				total=CMath.s_int(itemCode.substring(x+1));
			final Vector<Quad<Long,Integer,Integer,String>> list=new Vector<Quad<Long,Integer,Integer,String>>();
			int group=0;
			for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
			{
				final TickableGroup almostTock=e.next();
				int tick=0;
				for(final Iterator<TickClient> et=almostTock.tickers();et.hasNext();)
				{
					final TickClient C=et.next();
					if(C.getTickTotal()==0)
						continue;
					int i=0;
					for(;i<list.size();i++)
					{
						final Triad<Long,Integer,Integer> t=list.get(i);
						if(C.getMilliTotal()>=t.first.longValue())
						{
							list.add(i,new Quad<Long,Integer,Integer,String>(Long.valueOf(C.getMilliTotal()),Integer.valueOf(group),Integer.valueOf(tick),C.getClientObject().name()));
							break;
						}
					}
					if((list.size()==0)||((i>=list.size())&&(list.size()<total)))
						list.add(new Quad<Long,Integer,Integer,String>(Long.valueOf(C.getMilliTotal()),Integer.valueOf(group),Integer.valueOf(tick),C.getClientObject().name()));
					while(list.size()>total)
						list.remove(list.size()-1);
					tick++;
				}
				group++;
			}
			if(list.size()==0)
				return "";
			final StringBuilder str=new StringBuilder("");
			for(final Quad<Long,Integer,Integer,String> t : list)
				str.append(';').append(t.second).append(',').append(t.third);
			return str.toString().substring(1);
		}

		int totalTickers=0;
		long totalMillis=0;
		long totalTicks=0;
		int topGroupNumber=-1;
		long topGroupMillis=-1;
		long topGroupTicks=0;
		long topObjectMillis=-1;
		long topObjectTicks=0;
		int topObjectGroup=0;
		Tickable topObjectClient=null;
		int num=0;
		TickableGroup almostTock = null;
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			almostTock=e.next();
			totalTickers+=almostTock.numTickers();
			totalMillis+=almostTock.getMilliTotal();
			totalTicks+=almostTock.getTickTotal();
			if(almostTock.getMilliTotal()>topGroupMillis)
			{
				topGroupMillis=almostTock.getMilliTotal();
				topGroupTicks=almostTock.getTickTotal();
				topGroupNumber=num;
			}
			try
			{
				for(final Iterator<TickClient> et=almostTock.tickers();et.hasNext();)
				{
					final TickClient C=et.next();
					if(C.getMilliTotal()>topObjectMillis)
					{
						topObjectMillis=C.getMilliTotal();
						topObjectTicks=C.getTickTotal();
						topObjectClient=C.getClientObject();
						topObjectGroup=num;
					}
				}
			}
			catch(final NoSuchElementException ex)
			{
			}
			num++;
		}
		if(itemCode.equalsIgnoreCase("freeMemory"))
			return ""+(Runtime.getRuntime().freeMemory()/1000);
		else
		if(itemCode.equalsIgnoreCase("totalMemory"))
			return ""+(Runtime.getRuntime().totalMemory()/1000);
		else
		if(itemCode.equalsIgnoreCase("totalTime"))
			return ""+CMLib.english().stringifyElapsedTimeOrTicks(System.currentTimeMillis()-CMSecurity.getStartTime(),0);
		else
		if(itemCode.equalsIgnoreCase("startTime"))
			return CMLib.time().date2String(CMSecurity.getStartTime());
		else
		if(itemCode.equalsIgnoreCase("currentTime"))
			return CMLib.time().date2String(System.currentTimeMillis());
		else
		if(itemCode.equalsIgnoreCase("totalTickers"))
			return ""+totalTickers;
		else
		if(itemCode.equalsIgnoreCase("totalMillis"))
			return ""+totalMillis;
		else
		if(itemCode.equalsIgnoreCase("totalMillisTime"))
			return CMLib.english().stringifyElapsedTimeOrTicks(totalMillis,0);
		else
		if(itemCode.equalsIgnoreCase("totalMillisTimePlusAverage"))
			return CMLib.english().stringifyElapsedTimeOrTicks(totalMillis,totalTicks);
		else
		if(itemCode.equalsIgnoreCase("totalTicks"))
			return ""+totalTicks;
		else
		if(itemCode.equalsIgnoreCase("tickgroupsize"))
			return ""+allTicks.size();
		else
		if(itemCode.equalsIgnoreCase("numthreads"))
			return ""+getPoolExecutor(null).getPoolSize();
		else
		if(itemCode.equalsIgnoreCase("numactivethreads"))
			return ""+getPoolExecutor(null).getActiveCount();
		else
		if(itemCode.equalsIgnoreCase("topGroupNumber"))
			return ""+topGroupNumber;
		else
		if(itemCode.equalsIgnoreCase("topGroupMillis"))
			return ""+topGroupMillis;
		else
		if(itemCode.equalsIgnoreCase("topGroupMillisTime"))
			return CMLib.english().stringifyElapsedTimeOrTicks(topGroupMillis,0);
		else
		if(itemCode.equalsIgnoreCase("topGroupMillisTimePlusAverage"))
			return CMLib.english().stringifyElapsedTimeOrTicks(topGroupMillis,topGroupTicks);
		else
		if(itemCode.equalsIgnoreCase("topGroupTicks"))
			return ""+topGroupTicks;
		else
		if(itemCode.equalsIgnoreCase("topObjectMillis"))
			return ""+topObjectMillis;
		else
		if(itemCode.equalsIgnoreCase("topObjectMillisTime"))
			return CMLib.english().stringifyElapsedTimeOrTicks(topObjectMillis,0);
		else
		if(itemCode.equalsIgnoreCase("topObjectMillisTimePlusAverage"))
			return CMLib.english().stringifyElapsedTimeOrTicks(topObjectMillis,topObjectTicks);
		else
		if(itemCode.equalsIgnoreCase("topObjectTicks"))
			return ""+topObjectTicks;
		else
		if(itemCode.equalsIgnoreCase("topObjectGroup"))
			return ""+topObjectGroup;
		else
		if(itemCode.toLowerCase().startsWith("thread"))
		{
			final int xstart="thread".length();
			int xend=xstart;
			while((xend<itemCode.length())&&(Character.isDigit(itemCode.charAt(xend))))
				xend++;
			final int threadNum=CMath.s_int(itemCode.substring(xstart,xend));
			int curThreadNum=0;
			for(final Enumeration<CMLibrary> e=CMLib.libraries();e.hasMoreElements();)
			{
				final CMLibrary lib=e.nextElement();
				final TickClient serviceClient=lib.getServiceClient();
				if(serviceClient!=null)
				{
					if(curThreadNum==threadNum)
					{
						final String instrCode=itemCode.substring(xend);
						if(instrCode.equalsIgnoreCase("activeMiliTotal"))
							return ""+serviceClient.getMilliTotal();
						if(instrCode.equalsIgnoreCase("milliTotal"))
							return ""+serviceClient.getMilliTotal();
						if(instrCode.equalsIgnoreCase("status"))
							return ""+serviceClient.getStatus();
						if(instrCode.equalsIgnoreCase("name"))
							return ""+serviceClient.getName();
						if(instrCode.equalsIgnoreCase("MilliTotalTime"))
							return CMLib.english().stringifyElapsedTimeOrTicks(serviceClient.getMilliTotal(),0);
						if(instrCode.equalsIgnoreCase("MiliTotalTime"))
							return CMLib.english().stringifyElapsedTimeOrTicks(serviceClient.getMilliTotal(),0);
						if(instrCode.equalsIgnoreCase("MilliTotalTimePlusAverage")||instrCode.equalsIgnoreCase("MiliTotalTimePlusAverage"))
							return CMLib.english().stringifyElapsedTimeOrTicks(serviceClient.getMilliTotal(),serviceClient.getTickTotal());
						if(instrCode.equalsIgnoreCase("TickTotal"))
							return ""+serviceClient.getTickTotal();
						break;
					}
					curThreadNum++;
				}
			}

		}
		if(itemCode.equalsIgnoreCase("topObjectClient"))
		{
			if(topObjectClient!=null)
				return topObjectClient.name();
			return "";
		}

		return "";
	}

	@Override
	public void rejuv(final Room here, final int tickID)
	{
		TickableGroup almostTock=null;
		TickClient C=null;
		Tickable E2=null;
		final boolean doItems=((tickID<=0)||(tickID==Tickable.TICKID_ROOM_ITEM_REJUV));
		final boolean doMobs=((tickID<=0)||(tickID==Tickable.TICKID_MOB));
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			almostTock=e.next();
			try
			{
				for(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)
				{
					C=i.next();
					E2=C.getClientObject();
					if((doItems)
					&&(E2 instanceof ItemTicker)
					&&((here==null)||(((ItemTicker)E2).properLocation()==here)))
					{
						if(C.tickTicker(true))
							almostTock.delTicker(C);
					}
					else
					if((doMobs)
					&&(E2 instanceof MOB)
					&&(((MOB)E2).amDead())
					&&((here==null)||(((MOB)E2).getStartRoom()==here))
					&&(((MOB)E2).basePhyStats().rejuv()>0)
					&&(((MOB)E2).basePhyStats().rejuv()!=PhyStats.NO_REJUV)
					&&(((MOB)E2).phyStats().rejuv()>0))
					{
						((MOB)E2).phyStats().setRejuv(-1);
						if(C.tickTicker(true))
							almostTock.delTicker(C);
					}
				}
			}
			catch(final NoSuchElementException ex)
			{
			}
		}
	}

	@Override
	public List<TickClient> findTickClient(final String name, final boolean exactOnly)
	{
		final List<TickClient> clientsV=new Vector<TickClient>(1);
		TickableGroup almostTock=null;
		TickClient C=null;
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			almostTock=e.next();
			try
			{
				for(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)
				{
					C=i.next();
					final Tickable T=(C!=null)?C.getClientObject():null;
					if(T!=null)
					{
						if(exactOnly)
						{
							if(T.name().equalsIgnoreCase(name))
								clientsV.add(C);
						}
						else
						if(CMLib.english().containsString(T.name(), name))
							clientsV.add(C);
					}
				}
			}
			catch(final NoSuchElementException ex)
			{
			}
		}
		return clientsV;
	}

	@Override
	public void tickAllTickers(final Room here)
	{
		TickableGroup almostTock=null;
		TickClient C=null;
		Tickable E2=null;
		final WorldMap map=CMLib.map();
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			almostTock=e.next();
			try
			{
				for(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)
				{
					C=i.next();
					E2=C.getClientObject();
					if(here==null)
					{
						if(C.tickTicker(false))
							almostTock.delTicker(C);
					}
					else
					if(map.isHere(E2,here))
					{
						if(C.tickTicker(false))
							almostTock.delTicker(C);
					}
					else
					if((E2 instanceof Ability)
					&&(map.isHere(((Ability)E2).affecting(),here)))
					{
						if(C.tickTicker(false))
							almostTock.delTicker(C);
					}
				}
			}
			catch(final NoSuchElementException ex)
			{
			}
		}
	}

	@Override
	public void suspendResumeRecurse(final CMObject O, final boolean skipEmbeddedAreas, final boolean suspend)
	{
		if(O instanceof Item)
		{
			final Item I=(Item)O;
			suspendResumeTicking(I, -1, suspend);
			if((I instanceof Boardable)&&(!skipEmbeddedAreas))
				suspendResumeRecurse(((Boardable)I).getArea(),true,suspend);
		}
		else
		if(O instanceof MOB)
		{
			final MOB M=(MOB)O;
			for(int i=0;i<M.numItems();i++)
			{
				final Item I=M.getItem(i);
				suspendResumeRecurse(I,skipEmbeddedAreas,suspend);
			}
			final PlayerStats pStats=M.playerStats();
			if(pStats!=null)
			{
				final ItemCollection collection=pStats.getExtItems();
				if(collection!=null)
				for(int i=0;i<collection.numItems();i++)
				{
					final Item I=collection.getItem(i);
					suspendResumeRecurse(I,skipEmbeddedAreas,suspend);
				}
			}
			else
			{
				// dont suspend players -- they are handled differently
				suspendResumeTicking(M, -1, suspend);
			}
		}
		else
		if(O instanceof Room)
		{
			final Room R=(Room)O;
			suspendResumeTicking(R, -1, suspend);
			for(int i=0;i<R.numInhabitants();i++)
				suspendResumeRecurse(R.fetchInhabitant(i),skipEmbeddedAreas,suspend);
			for(int i=0;i<R.numItems();i++)
				suspendResumeRecurse(R.getItem(i),skipEmbeddedAreas,suspend);
		}
		else
		if(O instanceof Area)
		{
			final Area A=(Area)O;
			A.setAreaState(suspend?Area.State.FROZEN:Area.State.ACTIVE);
			for(final Enumeration<Room> r=A.getFilledProperMap();r.hasMoreElements();)
				suspendResumeRecurse(r.nextElement(),skipEmbeddedAreas,suspend);
		}
	}

	@Override
	public boolean deleteAllTicks(final Tickable ticker)
	{
		if(ticker==null)
			return false;
		deleteTick(ticker, -1);
		final WorldMap map=CMLib.map();
		boolean deleted = false;
		if(ticker instanceof MOB)
		{
			final MOB M=(MOB)ticker;
			for(int i=0;i<M.numItems();i++)
				deleteTick(M.getItem(i), -1);
		}
		if((ticker instanceof Physical)
		&&(!(ticker instanceof MOB)))
		{
			final Physical P=(Physical)ticker;
			for(int i=0;i<P.numEffects();i++)
				deleteTick(P.fetchEffect(i), -1);
		}
		if(ticker instanceof Room)
		{
			TickableGroup almostTock=null;
			TickClient C=null;
			Tickable E2=null;
			for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
			{
				almostTock=e.next();
				try
				{
					for(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)
					{
						C=i.next();
						E2=C.getClientObject();
						if(map.isHere(E2,(Room)ticker))
							deleted = almostTock.delTicker(C) || deleted;
						else
						if((E2 instanceof Ability)
						&&(map.isHere(((Ability)E2).affecting(),(Room)ticker)))
							deleted = almostTock.delTicker(C) || deleted;
						else
							continue;

						try
						{
							if(almostTock.numTickers()==0)
								allTicks.remove(almostTock);
						}
						catch (final Exception e2)
						{
						}
					}
				}
				catch(final NoSuchElementException ex)
				{
				}
			}
		}
		else
		if(ticker instanceof Area)
		{
			TickableGroup almostTock=null;
			TickClient C=null;
			Tickable E2=null;
			for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
			{
				almostTock=e.next();
				try
				{
					for(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)
					{
						C=i.next();
						E2=C.getClientObject();
						if(map.isHere(E2,(Area)ticker))
							deleted = almostTock.delTicker(C) || deleted;
						else
						if((E2 instanceof Ability)
						&&(map.isHere(((Ability)E2).affecting(),(Area)ticker)))
							deleted = almostTock.delTicker(C) || deleted;
						else
							continue;

						try
						{
							if(almostTock.numTickers()==0)
								allTicks.remove(almostTock);
						}
						catch (final Exception e2)
						{
						}
					}
				}
				catch(final NoSuchElementException ex)
				{
				}
			}
		}
		return deleted;
	}

	@Override
	public String getTickInfoReport(final String which)
	{
		int grpstart=-1;
		for(int i=0;i<which.length();i++)
		{
			if(Character.isDigit(which.charAt(i)))
			{
				grpstart=i;
				break;
			}
		}
		if(which.equalsIgnoreCase("tickGroupSize"))
			return ""+allTicks.size();
		else
		if(which.toLowerCase().startsWith("tickerssize"))
		{
			if(grpstart<0)
				return"";
			final int group=CMath.s_int(which.substring(grpstart));
			if((group>=0)&&(group<allTicks.size()))
			{
				final List<TickableGroup> enumeratedTicks=new XVector<TickableGroup>(allTicks);
				return ""+enumeratedTicks.get(group).numTickers();
			}
			return "";
		}
		int group=-1;
		int client=-1;
		final int clistart=which.indexOf('-');
		if((grpstart>=0)&&(clistart>grpstart))
		{
			group=CMath.s_int(which.substring(grpstart,clistart));
			client=CMath.s_int(which.substring(clistart+1));
		}

		if((group<0)||(client<0)||(group>=allTicks.size()))
			return "";
		final List<TickableGroup> enumeratedTicks=new XVector<TickableGroup>(allTicks);
		if((group<0)||(client<0)||(group>=enumeratedTicks.size()))
			return "";
		final TickableGroup almostTock=enumeratedTicks.get(group);

		if(client>=almostTock.numTickers())
			return "";
		final TickClient C=almostTock.fetchTickerByIndex(client);
		if(C==null)
			return "";

		if(which.toLowerCase().startsWith("tickername"))
		{
			Tickable E=C.getClientObject();
			if((E instanceof Ability)&&(E.ID().equals("ItemRejuv")))
				E=((Ability)E).affecting();
			if(E instanceof Room)
				return CMLib.map().getExtendedRoomID((Room)E);
			if(E!=null)
				return E.name();
			return "!NULL!";
		}
		else
		if(which.toLowerCase().startsWith("tickerid"))
			return ""+C.getTickID();
		else
		if(which.toLowerCase().startsWith("tickerstatusstr"))
			return C.getStatus();
		else
		if(which.toLowerCase().startsWith("tickerstatus"))
			return ((C.getClientObject()==null)?"":(""+C.getClientObject().getTickStatus()));
		else
		if(which.toLowerCase().startsWith("tickercodeword"))
			return getTickStatusSummary(C.getClientObject());
		else
		if(which.toLowerCase().startsWith("tickertickdown"))
			return ""+C.getCurrentTickDown();
		else
		if(which.toLowerCase().startsWith("tickerretickdown"))
			return ""+C.getTotalTickDown();
		else
		if(which.toLowerCase().startsWith("tickermillitotal"))
			return ""+C.getMilliTotal();
		else
		if(which.toLowerCase().startsWith("tickermilliavg"))
		{
			if(C.getTickTotal()==0)
				return "0";
			return ""+(C.getMilliTotal()/C.getTickTotal());
		}
		else
		if(which.toLowerCase().startsWith("tickerlaststartmillis"))
			return ""+C.getLastStartTime();
		else
		if(which.toLowerCase().startsWith("tickerlaststopmillis"))
			return ""+C.getLastStopTime();
		else
		if(which.toLowerCase().startsWith("tickerlaststartdate"))
			return CMLib.time().date2String(C.getLastStartTime());
		else
		if(which.toLowerCase().startsWith("tickerlaststopdate"))
			return CMLib.time().date2String(C.getLastStopTime());
		else
		if(which.toLowerCase().startsWith("tickerlastduration"))
		{
			if(C.getLastStopTime()>=C.getLastStartTime())
				return CMLib.english().stringifyElapsedTimeOrTicks(C.getLastStopTime()-C.getLastStartTime(),0);
			return CMLib.english().stringifyElapsedTimeOrTicks(System.currentTimeMillis()-C.getLastStartTime(),0);
		}
		else
		if(which.toLowerCase().startsWith("tickersuspended"))
			return ""+C.isSuspended();
		return "";
	}

	@Override
	public boolean shutdown()
	{
		//int numTicks=tickGroup.size();
		while(allTicks.size()>0)
		{
			//Log.sysOut("ServiceEngine","Shutting down all tick "+which+"/"+numTicks+"...");
			final TickableGroup tock=allTicks.iterator().next();
			if(tock!=null)
			{
				CMProps.setUpAllLowVar(CMProps.Str.MUDSTATUS,"Shutting down...shutting down Service Engine: killing "+tock.getName()+": "+tock.getStatus());
				tock.shutdown();
				allTicks.remove(tock);
			}
			CMLib.s_sleep(100);
		}
		CMProps.setUpAllLowVar(CMProps.Str.MUDSTATUS,"Shutting down...shutting down Service Engine: "+ID()+": thread shutdown");
		CMLib.killThread(drivingThread,100,10);
		// force final time tick!
		final Vector<TimeClock> timeObjects=new Vector<TimeClock>();
		for(final Enumeration<Area> e=CMLib.map().areas();e.hasMoreElements();)
		{
			final Area A=(e.nextElement());
			if(!timeObjects.contains(A.getTimeObj()))
				timeObjects.addElement(A.getTimeObj());
		}
		CMProps.setUpAllLowVar(CMProps.Str.MUDSTATUS,"Shutting down...shutting down Service Engine: "+ID()+": saving time objects");
		for(int t=0;t<timeObjects.size();t++)
			timeObjects.elementAt(t).save();
		for(final CMThreadPoolExecutor pool : threadPools)
		{
			if(pool != null)
			{
				pool.shutdown();
				try
				{
					pool.awaitTermination(2, TimeUnit.SECONDS);
				}
				catch (final InterruptedException e)
				{
					pool.shutdownNow();
					try
					{
						pool.awaitTermination(3, TimeUnit.SECONDS);
					}
					catch (final InterruptedException e2)
					{
					}
				}
			}
		}
		Log.sysOut("ServiceEngine","Shutdown complete.");
		return true;
	}

	@Override
	public synchronized void clearDebri(final Room room, final int taskCode)
	{
		TickableGroup almostTock=null;
		TickClient C=null;
		ItemTicker  I=null;
		Iterator<TickClient> roomSet;
		MOB mob=null;
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			almostTock=e.next();
			roomSet=almostTock.getLocalItems(taskCode,room);
			if(roomSet!=null)
			{
				for(;roomSet.hasNext();)
				{
					C=roomSet.next();
					if(C.getClientObject() instanceof ItemTicker)
					{
						I=(ItemTicker)C.getClientObject();
						almostTock.delTicker(C);
						I.setProperLocation(null);
					}
					else
					if(C.getClientObject() instanceof MOB)
					{
						mob=(MOB)C.getClientObject();
						if((mob.isMonster())
						&&(!room.isInhabitant(mob))
						&&((mob.amFollowing()==null)
							||(!mob.amUltimatelyFollowing().isPlayer())
							||(!CMLib.flags().isInTheGame(mob.amUltimatelyFollowing(), true))))
						{
							mob.destroy();
							almostTock.delTicker(C);
						}
					}
				}
			}
		}
	}

	@Override
	public List<Tickable> getNamedTickingObjects(String name)
	{
		final Vector<Tickable> V=new Vector<Tickable>();
		TickableGroup almostTock=null;
		TickClient C=null;
		name=name.toUpperCase().trim();
		for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
		{
			almostTock=e.next();
			for(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)
			{
				C=i.next();
				if((C.getClientObject()!=null)
				&&(C.getClientObject().name().toUpperCase().indexOf(name)>=0)
				&&(!V.contains(C.getClientObject())))
					V.addElement(C.getClientObject());
			}
		}
		return V;
	}

	@Override
	public String getTickStatusSummary(final Tickable obj)
	{
		if(obj==null)
			return "";
		final long code=obj.getTickStatus();
		if(obj instanceof Environmental)
		{
			if(CMath.bset(code,Tickable.STATUS_BEHAVIOR) && (obj instanceof PhysicalAgent))
			{
				final long b=(code-Tickable.STATUS_BEHAVIOR);
				String codeWord="Behavior #"+b;
				if((b>=0)&&(b<((PhysicalAgent)obj).numBehaviors()))
				{
					final Behavior B=((PhysicalAgent)obj).fetchBehavior((int)b);
					codeWord+=" ("+B.name()+": "+B.getTickStatus();
				}
				else
					codeWord+=" (#Error#)";
				return codeWord;
			}
			else
			if(CMath.bset(code,Tickable.STATUS_SCRIPT)&&(obj instanceof MOB))
			{
				final long b=(code-Tickable.STATUS_SCRIPT);
				String codeWord="Script #"+b;
				if((b>=0)&&(b<((MOB)obj).numScripts()))
				{
					final ScriptingEngine S=((MOB)obj).fetchScript((int)b);
					codeWord+=" ("+CMStrings.limit(S.getScript(),20)+": "+S.getTickStatus();
				}
				return codeWord;
			}
			else
			if((code&STATUS_ALLMISCTICKS)>0)
			{
				final long base=(code&STATUS_ALLMISCTICKS);
				int num=0;
				for(int i=1;i<6;i++)
				{
					if((1<<(10+i))==base)
					{
						num = i;
						break;
					}
				}
				return "Misc"+num+" Activity #"+(code-base);
			}
			else
			if(CMath.bset(code,Tickable.STATUS_AFFECT) && (obj instanceof Physical))
			{
				final long b=(code-Tickable.STATUS_AFFECT);
				String codeWord="Effect #"+b;
				if((b>=0)&&(b<((Physical)obj).numEffects()))
				{
					final Environmental E=((Physical)obj).fetchEffect((int)b);
					codeWord+=" ("+E.name()+": "+E.getTickStatus()+")";
				}
				return codeWord;
			}
		}
		String codeWord=null;
		if(CMath.bset(code,Tickable.STATUS_BEHAVIOR))
			codeWord="Behavior?!";
		else
		if(CMath.bset(code,Tickable.STATUS_SCRIPT))
			codeWord="Script?!";
		 else
		if(CMath.bset(code,Tickable.STATUS_AFFECT))
			codeWord="Effect?!";
		else
		switch((int)code)
		{
		case Tickable.STATUS_ALIVE:
			codeWord="Alive"; break;
		case Tickable.STATUS_REBIRTH:
			codeWord="Rebirth"; break;
		case Tickable.STATUS_CLASS:
			codeWord="Class"; break;
		case Tickable.STATUS_DEAD:
			codeWord="Dead"; break;
		case Tickable.STATUS_END:
			codeWord="End"; break;
		case Tickable.STATUS_FIGHT:
			codeWord="Fighting"; break;
		case Tickable.STATUS_NOT:
			codeWord="!"; break;
		case Tickable.STATUS_OTHER:
			codeWord="Other"; break;
		case Tickable.STATUS_RACE:
			codeWord="Race"; break;
		case Tickable.STATUS_START:
			codeWord="Start"; break;
		case Tickable.STATUS_WEATHER:
			codeWord="Weather"; break;
		default:
			codeWord="?"; break;
		}
		return codeWord;
	}

	public void insertOrderDeathInOrder(final DVector DV, final long lastStart, final String msg, final TickableGroup tock)
	{
		if(DV.size()>0)
		for(int i=0;i<DV.size();i++)
		{
			if(((Long)DV.elementAt(i,1)).longValue()>lastStart)
			{
				DV.insertElementAt(i,Long.valueOf(lastStart),msg,tock);
				return;
			}
		}
		DV.addElement(Long.valueOf(lastStart),msg,tock);
	}

	public void setSupportStatus(final String s)
	{
		if(supportClient != null)
		{
			supportClient.setStatus(s);
			if(CMSecurity.isDebugging(CMSecurity.DbgFlag.UTILITHREAD))
				Log.debugOut("ServiceEngine",s);
		}
	}

	@Override
	public void dumpDebugStack(final String ID, final Thread theThread)
	{
		// I wish Java had compiler directives.  Would be great to un-comment this for 1.5 JVMs
		final StringBuffer dump = new StringBuffer("");
		if(theThread == null)
			dump.append("NULL!!");
		else
		{
			final java.lang.StackTraceElement[] s=theThread.getStackTrace();
			for (final StackTraceElement element : s)
				dump.append("\n   "+element.getClassName()+": "+element.getMethodName()+"("+element.getFileName()+": "+element.getLineNumber()+")");
		}
		Log.debugOut(ID,dump.toString());
	}

	protected static void debugDumpThreadGroup(final ThreadGroup tGroup, final StringBuilder lines)
	{
		final int ac = tGroup.activeCount();
		final int agc = tGroup.activeGroupCount();
		final Thread tArray[] = new Thread [ac+1];
		final ThreadGroup tgArray[] = new ThreadGroup [agc+1];

		tGroup.enumerate(tArray,false);
		tGroup.enumerate(tgArray,false);

		lines.append(" ^HTGRP^?  ^H" + tGroup.getName() + "^?\n\r");
		for (int i = 0; i<ac; ++i)
		{
			if (tArray[i] != null)
			{
				lines.append(tArray[i].isAlive()? "  ok   " : " BAD!  ");
				lines.append(CMStrings.padRight(tArray[i].getName(),20)+": ");
				final String summary;
				if(tArray[i] instanceof MudHost)
					summary=CMClass.classID(tArray[i])+": "+((MudHost)tArray[i]).getStatus();
				else
					summary=tArray[i].toString();
				lines.append(summary+"\n\r");
				final java.lang.StackTraceElement[] s=tArray[i].getStackTrace();
				for (final StackTraceElement element : s)
					lines.append("   "+element.getClassName()+": "+element.getMethodName()+"("+element.getFileName()+": "+element.getLineNumber()+")\n\r");
			}
		}

		if (agc > 0)
		{
			lines.append("{\n\r");
			for (int i = 0; i<agc; ++i)
			{
				if (tgArray[i] != null)
					debugDumpThreadGroup(tgArray[i],lines);
			}
			lines.append("}\n\r");
		}
	}

	public final void checkHealth()
	{
		final long lastDateTime=System.currentTimeMillis()-SHORT_TICK_TIMEOUT;
		final long longerDateTime=System.currentTimeMillis()-LONG_TICK_TIMEOUT;
		setSupportStatus("checking");

		setSupportStatus("checking tick groups.");
		final DVector orderedDeaths=new DVector(3);
		try
		{
			TickableGroup almostTock = null;
			for(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)
			{
				almostTock=e.next();
				if((almostTock.isAwake())
				&&(almostTock.getLastStartTime()!=0)
				&&(almostTock.getLastStartTime()<lastDateTime))
				{
					final TickClient tickClient=almostTock.getLastTicked();
					final Tickable ticker=(tickClient!=null) ? tickClient.getClientObject() : null;
					if(tickClient==null)
						insertOrderDeathInOrder(orderedDeaths,0,"LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+"! No further information.",almostTock);
					else
					if((!CMath.bset(tickClient.getTickID(),Tickable.TICKID_LONGERMASK))||(almostTock.getLastStopTime()<longerDateTime))
					{
						if(ticker==null)
							insertOrderDeathInOrder(orderedDeaths,0,"LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+": NULL @"+CMLib.time().date2String(tickClient.getLastStartTime())+", tickID "+tickClient.getTickID(),almostTock);
						else
						{
							StringBuffer logError=null;
							final long code=ticker.getTickStatus();
							final String codeWord=getTickStatusSummary(ticker);
							String log=null;
							if(ticker instanceof Environmental)
								logError=new StringBuffer("LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+": "+ticker.name()+" ("+((Environmental)ticker).ID()+") @"+CMLib.time().date2String(tickClient.getLastStartTime())+", status("+code+" ("+codeWord+"), tickID "+tickClient.getTickID());
							else
								logError=new StringBuffer("LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+": "+ticker.name()+", status ("+code+"/"+codeWord+") @"+CMLib.time().date2String(tickClient.getLastStartTime())+", tickID "+tickClient.getTickID());

							if((ticker instanceof MOB)&&(((MOB)ticker).location()!=null))
								log=logError.toString()+" in "+((MOB)ticker).location().roomID();
							else
							if((ticker instanceof Item)&&(((Item)ticker).owner()!=null)&&(((Item)ticker).owner() instanceof Room))
								log=logError.toString()+" in "+((Room)((Item)ticker).owner()).roomID();
							else
							if((ticker instanceof Item)&&(((Item)ticker).owner()!=null)&&(((Item)ticker).owner() instanceof MOB))
								log=logError.toString()+" owned by "+((MOB)((Item)ticker).owner()).name();
							else
							if(ticker instanceof Room)
								log=logError.toString()+" is "+((Room)ticker).roomID();
							else
								log=logError.toString();
							insertOrderDeathInOrder(orderedDeaths,tickClient.getLastStartTime(),log,almostTock);
						}
					}
					// no isDEBUGGING check -- just always let her rip.
					dumpDebugStack(Thread.currentThread().getName(),almostTock.getCurrentThread());
				}
			}
		}
		catch (final java.util.NoSuchElementException e)
		{
		}
		for(int i=0;i<orderedDeaths.size();i++)
			Log.errOut(Thread.currentThread().getName(),(String)orderedDeaths.elementAt(i,2));

		setSupportStatus("killing tick groups.");
		for(int x=0;x<orderedDeaths.size();x++)
		{
			final TickableGroup almostTock=(TickableGroup)orderedDeaths.elementAt(x,3);
			final Vector<TickClient> tockClients=new Vector<TickClient>();
			try
			{
				for(final Iterator<TickClient> e=almostTock.tickers();e.hasNext();)
					tockClients.addElement(e.next());
			}
			catch (final NoSuchElementException e)
			{
			}
			try
			{
				almostTock.shutdown();
			}
			catch(final java.lang.ThreadDeath d)
			{
				Log.errOut("ThreadDeath killing "+almostTock.getName());
			}
			catch(final Throwable t)
			{
				Log.errOut("Error "+t.getMessage()+" killing "+almostTock.getName());
			}
			if(CMLib.threads() instanceof ServiceEngine)
				((ServiceEngine)CMLib.threads()).delTickGroup(almostTock);
			for(int i=0;i<tockClients.size();i++)
			{
				final TickClient c=tockClients.elementAt(i);
				startTickDown(c.getClientObject(),c.getTickID(),c.getTotalTickDown());
			}
		}

		setSupportStatus("Checking mud threads");
		for(int m=0;m<CMLib.hosts().size();m++)
		{
			final List<Runnable> badThreads=CMLib.hosts().get(m).getOverdueThreads();
			for(final Runnable T : badThreads)
			{
				if(T instanceof Thread)
				{
					String threadName=((Thread)T).getName();
					if(T instanceof Tickable)
						threadName=((Tickable)T).name()+" ("+((Tickable)T).ID()+"): "+((Tickable)T).getTickStatus();
					setSupportStatus("Killing "+threadName);
					Log.errOut("Killing stray thread: "+threadName);
					CMLib.killThread((Thread)T,100,1);
				}
				else
					Log.errOut("Unable to kill stray runnable: "+T.toString());
			}
		}
		setSupportStatus("Done checking threads");
	}

	@Override
	public void run()
	{
		while(!CMProps.getBoolVar(CMProps.Bool.MUDSTARTED))
			CMLib.s_sleep(1000);
		final CMProps props = CMProps.instance();
		final CMProps.Bool MUDSHUTTINGDOWN=CMProps.Bool.MUDSHUTTINGDOWN;
		while(!props.getBool(MUDSHUTTINGDOWN))
		{
			try
			{
				while(isAllSuspended() && (!props.getBool(MUDSHUTTINGDOWN)))
				{
					if((unsuspendedRunnables!=null)&&(unsuspendedRunnables.length>0))
					{
						Thread.sleep(100);
						for(final CMRunnable runnable : unsuspendedRunnables)
						{
							try
							{
								runnable.run();
							}
							catch(final Exception e)
							{
								Log.errOut(e);
							}
						}
					}
					else
						Thread.sleep(2000);
				}
				final long now=System.currentTimeMillis();
				globalTickID = (now - globalStartTime) / props.tickMillis();
				long nextWake=now + 3600000;
				if(this.schedTicks.size() > 0)
				{
					final List<CMRunnable> runThese = new LinkedList<CMRunnable>();
					synchronized(this.schedTicks)
					{
						if(this.schedTicks.size() > 0)
						{
							for(final Iterator<CMRunnable> r=this.schedTicks.iterator(); r.hasNext();)
							{
								final CMRunnable R=r.next();
								if(now >= R.getStartTime())
								{
									runThese.add(R);
									r.remove();
								}
								else
								if(R.getStartTime() < nextWake)
									nextWake = R.getStartTime();
							}
						}
					}
					for(final CMRunnable R : runThese)
						getPoolExecutor((char)R.getGroupID()).execute(R);
				}
				synchronized(allTicks)
				{
					for(final TickableGroup T : allTicks)
					{
						if(!T.isAwake() && (!getPoolExecutor(T.getThreadGroupName()).isActiveOrQueued(T)))
						{
							if (T.getNextTickTime() <= now)
							{
								if(now + T.getTickInterval() < nextWake)
									nextWake = now + T.getTickInterval();
								getPoolExecutor(T.getThreadGroupName()).execute(T);
							}
							else
							if (nextWake > T.getNextTickTime())
							{
								nextWake = T.getNextTickTime();
							}
						}
						else
						if(now + T.getTickInterval() < nextWake)
							nextWake = now + T.getTickInterval();
					}
				}
				if(nextWake > now)
				{
					nextWakeAtTime = nextWake;
					Thread.sleep(nextWake-now);
				}
			}
			catch(final InterruptedException e)
			{
			}
			catch(final Exception e)
			{
				Log.errOut("Scheduler",e);
			}
		}
		threadPools=new CMThreadPoolExecutor[256];
		drivingThread=null;
		supportClient=null;
		Log.sysOut("ServiceEngine","Scheduler stopped");
	}

	@Override
	public boolean activate()
	{
		getPoolExecutor(null); // will cause the creation

		if(supportClient==null)
		supportClient=startTickDown(null,new Tickable()
		{
			private int tickStatus = Tickable.STATUS_NOT;

				@Override
				public String ID()
				{
					return "THThreads";
				}

				@Override
				public CMObject newInstance()
				{
					return this;
				}

				@Override
				public CMObject copyOf()
				{
					return this;
				}

				@Override
				public void initializeClass()
				{
				}

				@Override
				public int compareTo(final CMObject o)
				{
					return (o == this) ? 0 : 1;
				}

				@Override
				public String name()
				{
					return ID();
				}

				@Override
				public int getTickStatus()
				{
					return tickStatus;
				}

				@Override
				public boolean tick(final Tickable ticking, final int tickID)
				{
				if((!CMSecurity.isDisabled(CMSecurity.DisFlag.UTILITHREAD))
				&&(!CMSecurity.isDisabled(CMSecurity.DisFlag.THREADTHREAD)))
				{
					tickStatus=Tickable.STATUS_ALIVE;
					checkHealth();
					Resources.removeResource("SYSTEM_HASHED_MASKS");
					tickStatus=Tickable.STATUS_NOT;
					setSupportStatus("Sleeping");
				}
				return true;
			}
		}, Tickable.TICKID_MISCELLANEOUS|Tickable.TICKID_SOLITARYMASK, MudHost.TIME_UTILTHREAD_SLEEP, 1);
		if(drivingThread==null)
		{
			drivingThread=new Thread(Thread.currentThread().getThreadGroup(),this,"Scheduler"+Thread.currentThread().getThreadGroup().getName().charAt(0));
			drivingThread.setDaemon(true);
			drivingThread.start();
		}
		return true;
	}

	public static void panicDumpAllThreads()
	{
		ThreadGroup topTG = Thread.currentThread().getThreadGroup();
		while (topTG != null && topTG.getParent() != null)
			topTG = topTG.getParent();
		final StringBuilder str=new StringBuilder("\n\r\n\rThread Dump:\n\r");
		if (topTG != null)
		{
			debugDumpThreadGroup(topTG,str);
		}
		final PrintStream sysOut=System.out;
		final PrintStream errOut=System.out;
		errOut.println(str.toString());
		sysOut.println(str.toString());
		Log.errOut(str.toString());
	}
}

<MSG> group assign fix.

git-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@17453 0d6f1817-ed0e-0410-87c9-987e46238f29

<DFF> @@ -320,12 +320,12 @@ public class ServiceEngine implements ThreadEngine
 	}
 
 	@Override
-	public TickClient startTickDown(final Tickable E, final int tickID, final long TICK_TIME, final int numTicks)
+	public TickClient startTickDown(final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)
 	{
-		return startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,TICK_TIME,numTicks);
+		return startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,tickTimeMs,numTicks);
 	}
 
-	public synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTime, final int numTicks)
+	public synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)
 	{
 		TickableGroup tock=null;
 		if(group==null)
@@ -340,7 +340,7 @@ public class ServiceEngine implements ThreadEngine
 				return null;
 			}
 			if((tock==null)
-			&&(almostTock.getTickInterval()==tickTime)
+			&&(almostTock.getTickInterval()==tickTimeMs)
 			&&(!almostTock.isSolitaryTicker())
 			&&(almostTock.numTickers()<getMaxObjectsPerThread()))
 			{
@@ -353,7 +353,7 @@ public class ServiceEngine implements ThreadEngine
 		final boolean isSolitary = ((tickID&Tickable.TICKID_SOLITARYMASK)==Tickable.TICKID_SOLITARYMASK);
 		if((tock==null)||isSolitary)
 		{
-			tock=new StdTickGroup(this, tickTime, Thread.currentThread().getThreadGroup().getName(), isSolitary);
+			tock=new StdTickGroup(this, tickTimeMs, group.getName(), isSolitary);
 			addTickGroup(tock);
 		}
 

{'commit': 'diff --git a/com/planet_ink/coffee_mud/core/threads/ServiceEngine.java b/com/planet_ink/coffee_mud/core/threads/ServiceEngine.java\nindex 5defd5ab5a..1313350933 100644\n--- a/com/planet_ink/coffee_mud/core/threads/ServiceEngine.java\n+++ b/com/planet_ink/coffee_mud/core/threads/ServiceEngine.java\n@@ -320,12 +320,12 @@ public class ServiceEngine implements ThreadEngine\n \t}\r\n \r\n \t@Override\r\n-\tpublic TickClient startTickDown(final Tickable E, final int tickID, final long TICK_TIME, final int numTicks)\r\n+\tpublic TickClient startTickDown(final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)\r\n \t{\r\n-\t\treturn startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,TICK_TIME,numTicks);\r\n+\t\treturn startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,tickTimeMs,numTicks);\r\n \t}\r\n \r\n-\tpublic synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTime, final int numTicks)\r\n+\tpublic synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)\r\n \t{\r\n \t\tTickableGroup tock=null;\r\n \t\tif(group==null)\r\n@@ -340,7 +340,7 @@ public class ServiceEngine implements ThreadEngine\n \t\t\t\treturn null;\r\n \t\t\t}\r\n \t\t\tif((tock==null)\r\n-\t\t\t&&(almostTock.getTickInterval()==tickTime)\r\n+\t\t\t&&(almostTock.getTickInterval()==tickTimeMs)\r\n \t\t\t&&(!almostTock.isSolitaryTicker())\r\n \t\t\t&&(almostTock.numTickers()<getMaxObjectsPerThread()))\r\n \t\t\t{\r\n@@ -353,7 +353,7 @@ public class ServiceEngine implements ThreadEngine\n \t\tfinal boolean isSolitary = ((tickID&Tickable.TICKID_SOLITARYMASK)==Tickable.TICKID_SOLITARYMASK);\r\n \t\tif((tock==null)||isSolitary)\r\n \t\t{\r\n-\t\t\ttock=new StdTickGroup(this, tickTime, Thread.currentThread().getThreadGroup().getName(), isSolitary);\r\n+\t\t\ttock=new StdTickGroup(this, tickTimeMs, group.getName(), isSolitary);\r\n \t\t\taddTickGroup(tock);\r\n \t\t}\r\n \r\n', 'commit_subject': 'group assign fix.', 'repo_name': 'bozimmerman/CoffeeMud', 'committer': {'name': 'bozimmerman', 'email': 'dc45fe023482b3b890b5e574e53c1d051f45b1ef@zimmers.net', 'time_sec': '1544296290', 'tz_offset': '0', 'date': {'seconds': '1544296290'}}, 'commit_tree': 'd4226b6b0c98154d65d446f5bbc450469f7d6c9b', 'commit_message': 'group assign fix.\n\ngit-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@17453 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'language_name': 'Shell', 'license': 'apache-2.0', 'hunks': '@@ -320,12 +320,12 @@ public class ServiceEngine implements ThreadEngine\n \t}\r\n \r\n \t@Override\r\n-\tpublic TickClient startTickDown(final Tickable E, final int tickID, final long TICK_TIME, final int numTicks)\r\n+\tpublic TickClient startTickDown(final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)\r\n \t{\r\n-\t\treturn startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,TICK_TIME,numTicks);\r\n+\t\treturn startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,tickTimeMs,numTicks);\r\n \t}\r\n \r\n-\tpublic synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTime, final int numTicks)\r\n+\tpublic synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)\r\n \t{\r\n \t\tTickableGroup tock=null;\r\n \t\tif(group==null)\r\n@@ -340,7 +340,7 @@ public class ServiceEngine implements ThreadEngine\n \t\t\t\treturn null;\r\n \t\t\t}\r\n \t\t\tif((tock==null)\r\n-\t\t\t&&(almostTock.getTickInterval()==tickTime)\r\n+\t\t\t&&(almostTock.getTickInterval()==tickTimeMs)\r\n \t\t\t&&(!almostTock.isSolitaryTicker())\r\n \t\t\t&&(almostTock.numTickers()<getMaxObjectsPerThread()))\r\n \t\t\t{\r\n@@ -353,7 +353,7 @@ public class ServiceEngine implements ThreadEngine\n \t\tfinal boolean isSolitary = ((tickID&Tickable.TICKID_SOLITARYMASK)==Tickable.TICKID_SOLITARYMASK);\r\n \t\tif((tock==null)||isSolitary)\r\n \t\t{\r\n-\t\t\ttock=new StdTickGroup(this, tickTime, Thread.currentThread().getThreadGroup().getName(), isSolitary);\r\n+\t\t\ttock=new StdTickGroup(this, tickTimeMs, group.getName(), isSolitary);\r\n \t\t\taddTickGroup(tock);\r\n \t\t}\r\n \r\n', 'addition_count': 5, 'deletion_count': 5, 'src_file': 'a/com/planet_ink/coffee_mud/core/threads/ServiceEngine.java', 'tgt_file': 'b/com/planet_ink/coffee_mud/core/threads/ServiceEngine.java', 'file_extension': '.java', 'before_file': 'package com.planet_ink.coffee_mud.core.threads;\nimport com.planet_ink.coffee_mud.core.database.DBInterface;\nimport com.planet_ink.coffee_mud.core.interfaces.*;\nimport com.planet_ink.coffee_mud.core.*;\nimport com.planet_ink.coffee_mud.core.CMLib.Library;\nimport com.planet_ink.coffee_mud.core.CMProps.Str;\nimport com.planet_ink.coffee_mud.core.collections.*;\nimport com.planet_ink.coffee_mud.Abilities.interfaces.*;\nimport com.planet_ink.coffee_mud.Areas.interfaces.*;\nimport com.planet_ink.coffee_mud.Behaviors.interfaces.*;\nimport com.planet_ink.coffee_mud.CharClasses.interfaces.*;\nimport com.planet_ink.coffee_mud.Commands.interfaces.*;\nimport com.planet_ink.coffee_mud.Common.interfaces.*;\nimport com.planet_ink.coffee_mud.Exits.interfaces.*;\nimport com.planet_ink.coffee_mud.Items.interfaces.*;\nimport com.planet_ink.coffee_mud.Locales.interfaces.*;\nimport com.planet_ink.coffee_mud.MOBS.interfaces.*;\nimport com.planet_ink.coffee_mud.Races.interfaces.*;\n\nimport java.io.File;\nimport java.io.PrintStream;\nimport java.util.*;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport com.planet_ink.coffee_mud.Libraries.interfaces.*;\nimport com.planet_ink.coffee_web.interfaces.HTTPRequest;\n\n/*\n   Copyright 2001-2022 Bo Zimmerman\n\n   Licensed under the Apache License, Version 2.0 (the "License");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n\t   http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an "AS IS" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n*/\npublic class ServiceEngine implements ThreadEngine\n{\n\tpublic static final  long STATUS_ALLMISCTICKS=Tickable.STATUS_MISC|Tickable.STATUS_MISC2|Tickable.STATUS_MISC3|Tickable.STATUS_MISC4|Tickable.STATUS_MISC5|Tickable.STATUS_MISC6;\n\tprivate static final long SHORT_TICK_TIMEOUT = (5*TimeManager.MILI_MINUTE);\n\tprivate static final long LONG_TICK_TIMEOUT  = (120*TimeManager.MILI_MINUTE);\n\n\tprivate Thread  \t\t\t\tdrivingThread\t\t= null;\n\tprivate TickClient \t\t\t\tsupportClient\t\t= null;\n\tprotected List<TickableGroup>\tallTicks\t\t\t= new SLinkedList<TickableGroup>();\n\tprivate boolean \t\t\t\tisSuspended\t\t\t= false;\n\tprivate int \t\t\t\t\tmax_objs_per_thread\t= 0;\n\tprivate CMThreadPoolExecutor[]\tthreadPools\t\t\t= new CMThreadPoolExecutor[256];\n\tprivate volatile long\t\t\tglobalTickID\t\t= 0;\n\tprivate final long \t\t\t\tglobalStartTime\t\t= System.currentTimeMillis();\n\tprivate volatile CMRunnable[]\tunsuspendedRunnables= null;\n\tprivate volatile long\t\t\tnextWakeAtTime\t\t= 0;\n\tprivate final List<CMRunnable>\tschedTicks\t\t\t= new LinkedList<CMRunnable>();\n\n\t@Override\n\tpublic String ID()\n\t{\n\t\treturn "ServiceEngine";\n\t}\n\n\t@Override\n\tpublic String name()\n\t{\n\t\treturn ID();\n\t}\n\n\t@Override\n\tpublic CMObject newInstance()\n\t{\n\t\ttry\n\t\t{\n\t\t\treturn getClass().newInstance();\n\t\t}\n\t\tcatch (final Exception e)\n\t\t{\n\t\t\treturn new ServiceEngine();\n\t\t}\n\t}\n\n\t@Override\n\tpublic String L(final String str, final String... xs)\n\t{\n\t\treturn CMLib.lang().fullSessionTranslation(str, xs);\n\t}\n\n\t@Override\n\tpublic void initializeClass()\n\t{\n\t}\n\n\t@Override\n\tpublic CMObject copyOf()\n\t{\n\t\ttry\n\t\t{\n\t\t\treturn (CMObject) this.clone();\n\t\t}\n\t\tcatch (final Exception e)\n\t\t{\n\t\t\treturn newInstance();\n\t\t}\n\t}\n\n\t@Override\n\tpublic int compareTo(final CMObject o)\n\t{\n\t\treturn CMClass.classID(this).compareToIgnoreCase(CMClass.classID(o));\n\t}\n\n\t@Override\n\tpublic void propertiesLoaded()\n\t{\n\t}\n\n\t@Override\n\tpublic TickClient getServiceClient()\n\t{\n\t\treturn supportClient;\n\t}\n\n\tpublic ServiceEngine()\n\t{\n\t\tinitializeClass();\n\t}\n\n\tprotected CMThreadPoolExecutor getPoolExecutor(final char threadGroupNum)\n\t{\n\t\tfinal CMThreadPoolExecutor pool = threadPools[threadGroupNum];\n\t\tif(pool != null)\n\t\t\treturn pool;\n\t\tfinal int minThreads = CMProps.getIntVar(CMProps.Int.MINWORKERTHREADS);\n\t\tint maxThreads = CMProps.getIntVar(CMProps.Int.MAXWORKERTHREADS);\n\t\tif(maxThreads<=0)\n\t\t\tmaxThreads=Integer.MAX_VALUE;\n\t\tfinal String sessionThreadGroupName="Worker"+threadGroupNum;\n\t\tthreadPools[threadGroupNum] = new CMThreadPoolExecutor(sessionThreadGroupName,minThreads, maxThreads, CMProps.getTickMillis()*2, TimeUnit.MILLISECONDS, (LONG_TICK_TIMEOUT/60000), 1024);\n\t\tthreadPools[threadGroupNum].setThreadFactory(new CMThreadFactory(sessionThreadGroupName));\n\t\treturn threadPools[threadGroupNum];\n\t}\n\n\tprotected CMThreadPoolExecutor getPoolExecutor(final String threadGroupName)\n\t{\n\t\tif(threadGroupName == null)\n\t\t\treturn getPoolExecutor(Thread.currentThread().getThreadGroup().getName().charAt(0));\n\t\telse\n\t\t\treturn getPoolExecutor(threadGroupName.charAt(0));\n\t}\n\n\t@Override\n\tpublic Iterator<TickableGroup> tickGroups()\n\t{\n\t\treturn allTicks.iterator();\n\t}\n\n\t@Override\n\tpublic Runnable findRunnableByThread(final Thread thread)\n\t{\n\t\tif((thread==null)||(threadPools==null))\n\t\t\treturn null;\n\t\tfinal Runnable possR=(thread instanceof CMFactoryThread)?((CMFactoryThread)thread).getRunnable():null;\n\t\tfor (final CMThreadPoolExecutor threadPool : threadPools)\n\t\t{\n\t\t\tfinal CMThreadPoolExecutor executor=threadPool;\n\t\t\tif(executor==null)\n\t\t\t\tcontinue;\n\t\t\tif(possR!=null)\n\t\t\t{\n\t\t\t\tsynchronized(executor.active)\n\t\t\t\t{\n\t\t\t\t\tif(executor.active.get(possR)==thread)\n\t\t\t\t\t\treturn possR;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif((executor.getThreadFactory() instanceof CMThreadFactory)\n\t\t\t&&(!((CMThreadFactory)executor.getThreadFactory()).getThreads().contains(thread)))\n\t\t\t\tcontinue;\n\t\t\tsynchronized(executor.active)\n\t\t\t{\n\t\t\t\tif(!executor.active.containsValue(thread))\n\t\t\t\t\tcontinue;\n\t\t\t\tfor(final Map.Entry<Runnable, Thread> e : executor.active.entrySet())\n\t\t\t\t{\n\t\t\t\t\tif(e.getValue()==thread)\n\t\t\t\t\t\treturn e.getKey();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n    public void scheduleRunnable(final Runnable R, final long ellapsedMs)\n\t{\n\t\ttry\n\t\t{\n\t\t\tsynchronized(this.schedTicks)\n\t\t\t{\n\t\t\t\tfinal long myNextTime = System.currentTimeMillis() + ellapsedMs;\n\t\t\t\tfinal char currentThreadId = Thread.currentThread().getThreadGroup().getName().charAt(0);\n\t\t\t\tschedTicks.add(new CMRunnable()\n\t\t\t\t{\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run()\n\t\t\t\t\t{\n\t\t\t\t\t\ttry\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tR.run();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch(final Throwable t)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tLog.errOut(t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic long activeTimeMillis()\n\t\t\t\t\t{\n\t\t\t\t\t\treturn System.currentTimeMillis() - getStartTime();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic long getStartTime()\n\t\t\t\t\t{\n\t\t\t\t\t\treturn myNextTime;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic int getGroupID()\n\t\t\t\t\t{\n\t\t\t\t\t\treturn currentThreadId;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif((this.nextWakeAtTime == 0) || (myNextTime < this.nextWakeAtTime))\n\t\t\t\t{\n\t\t\t\t\tthis.nextWakeAtTime = myNextTime;\n\t\t\t\t\tif(drivingThread!=null)\n\t\t\t\t\t\tdrivingThread.interrupt();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch(final Exception e)\n\t\t{\n\t\t\tLog.errOut("ServiceEngine","ExecRun: "+e.getMessage());\n\t\t\tLog.debugOut("ServiceEngine",e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executeRunnable(final String threadGroupName, final Runnable R)\n\t{\n\t\ttry\n\t\t{\n\t\t\tgetPoolExecutor(threadGroupName).execute(R);\n\t\t}\n\t\tcatch(final Exception e)\n\t\t{\n\t\t\tLog.errOut("ServiceEngine","ExecRun: "+e.getMessage());\n\t\t\tLog.debugOut("ServiceEngine",e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executeRunnable(final char threadGroupId, final Runnable R)\n\t{\n\t\ttry\n\t\t{\n\t\t\tgetPoolExecutor(threadGroupId).execute(R);\n\t\t}\n\t\tcatch(final Exception e)\n\t\t{\n\t\t\tLog.errOut("ServiceEngine","ExecRun: "+e.getMessage());\n\t\t\tLog.debugOut("ServiceEngine",e);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void executeRunnable(final Runnable R)\n\t{\n\t\ttry\n\t\t{\n\t\t\tgetPoolExecutor(null).execute(R);\n\t\t}\n\t\tcatch(final Exception e)\n\t\t{\n\t\t\tLog.errOut("ServiceEngine","ExecRun: "+e.getMessage());\n\t\t\tLog.debugOut("ServiceEngine",e);\n\t\t}\n\t}\n\n\tpublic int getMaxObjectsPerThread()\n\t{\n\t\tif(max_objs_per_thread>0)\n\t\t\treturn max_objs_per_thread;\n\t\tmax_objs_per_thread = CMProps.getIntVar(CMProps.Int.OBJSPERTHREAD);\n\t\tif(max_objs_per_thread>0)\n\t\t\treturn max_objs_per_thread;\n\t\tmax_objs_per_thread=0;\n\t\treturn 128;\n\t}\n\n\tprotected long getTicksEllapsedSinceStartup()\n\t{\n\t\treturn globalTickID;\n\t}\n\n\tprotected void delTickGroup(final TickableGroup tock)\n\t{\n\t\tallTicks.remove(tock);\n\t\tif(drivingThread!=null)\n\t\t\tdrivingThread.interrupt();\n\t}\n\t}\r\n\r\n\t@Override\r\n\tpublic TickClient startTickDown(final Tickable E, final int tickID, final long TICK_TIME, final int numTicks)\r\n\t{\r\n\t\treturn startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,TICK_TIME,numTicks);\r\n\t}\r\n\r\n\tpublic synchronized TickClient startTickDown(ThreadGroup group, final Tickable E, final int tickID, final long tickTime, final int numTicks)\r\n\t{\r\n\t\tTickableGroup tock=null;\r\n\t\tif(group==null)\r\n\t\treturn startTickDown(E,tickID,CMProps.getTickMillis(),numTicks);\n\t}\n\n\t@Override\n\tpublic TickClient startTickDown(final Tickable E, final int tickID, final long tickTimeMs, final int numTicks)\n\t{\n\t\treturn startTickDown(CMLib.map().getOwnedThreadGroup(E),E,tickID,tickTimeMs,numTicks);\n\t}\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\tif((tock==null)\r\n\t\t\t&&(almostTock.getTickInterval()==tickTime)\r\n\t\t\t&&(!almostTock.isSolitaryTicker())\r\n\t\t\t&&(almostTock.numTickers()<getMaxObjectsPerThread()))\r\n\t\t\t{\r\n\t\tfor(final TickableGroup almostTock : allTicks)\n\t\t{\n\t\t\tif(almostTock.contains(E,tickID))\n\t\t\t{\n\t\t\t\treturn almostTock.getTickInterval();\n\t\t\t}\n\t\tfinal boolean isSolitary = ((tickID&Tickable.TICKID_SOLITARYMASK)==Tickable.TICKID_SOLITARYMASK);\r\n\t\tif((tock==null)||isSolitary)\r\n\t\t{\r\n\t\t\ttock=new StdTickGroup(this, tickTime, Thread.currentThread().getThreadGroup().getName(), isSolitary);\r\n\t\t\taddTickGroup(tock);\r\n\t\t}\r\n\r\n\t\tif(group==null)\n\t\t\tgroup=Thread.currentThread().getThreadGroup();\n\t\tfinal char threadGroupNum=group.getName().charAt(0);\n\t\tfor(final TickableGroup almostTock : allTicks)\n\t\t{\n\t\t\tif(almostTock.contains(E,tickID))\n\t\t\t{\n\t\t\t\tfor(final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);set.hasNext();)\n\t\t\t\t\tset.next().setSuspended(false);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif((tock==null)\n\t\t\t&&(almostTock.getTickInterval()==tickTimeMs)\n\t\t\t&&(!almostTock.isSolitaryTicker())\n\t\t\t&&(almostTock.numTickers()<getMaxObjectsPerThread()))\n\t\t\t{\n\t\t\t\tfinal String name = almostTock.getThreadGroupName();\n\t\t\t\tif((name!=null)\n\t\t\t\t&&(name.charAt(0)==threadGroupNum))\n\t\t\t\t\ttock=almostTock;\n\t\t\t}\n\t\t}\n\t\tfinal boolean isSolitary = ((tickID&Tickable.TICKID_SOLITARYMASK)==Tickable.TICKID_SOLITARYMASK);\n\t\tif((tock==null)||isSolitary)\n\t\t{\n\t\t\ttock=new StdTickGroup(this, tickTimeMs, group.getName(), isSolitary);\n\t\t\taddTickGroup(tock);\n\t\t}\n\n\t\tfinal TickClient newC=new StdTickClient(E,numTicks,tickID);\n\t\ttock.addTicker(newC);\n\t\treturn newC;\n\t}\n\n\t@Override\n\tpublic synchronized boolean deleteTick(final Tickable E, final int tickID)\n\t{\n\t\tboolean foundOne=false;\n\t\tfor(final TickableGroup almostTock : allTicks)\n\t\t{\n\t\t\tfinal Iterator<TickClient> set=almostTock.getTickSet(E,tickID);\n\t\t\tfoundOne = foundOne || set.hasNext();\n\t\t\tfor(;set.hasNext();)\n\t\t\t\talmostTock.delTicker(set.next());\n\t\t}\n\t\treturn foundOne;\n\t}\n\n\t@Override\n\tpublic synchronized boolean setTickPending(final Tickable E, final int tickID)\n\t{\n\t\tboolean foundOne=false;\n\t\tfor(final TickableGroup almostTock : allTicks)\n\t\t{\n\t\t\tfinal Iterator<TickClient> set=almostTock.getTickSet(E,tickID);\n\t\t\tfoundOne = foundOne || set.hasNext();\n\t\t\tfor(;set.hasNext();)\n\t\t\t{\n\t\t\t\tfinal TickClient C = set.next();\n\t\t\t\tC.setCurrentTickDownPending();\n\t\t\t}\n\t\t}\n\t\treturn foundOne;\n\t}\n\n\t@Override\n\tpublic long getTimeMsToNextTick(final Tickable E, final int tickID)\n\t{\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\tfinal TickableGroup almostTock=e.next();\n\t\t\tfinal Iterator<TickClient> set=almostTock.getTickSet(E,tickID);\n\t\t\tif(set.hasNext())\n\t\t\t{\n\t\t\t\tfinal TickClient client = set.next();\n\t\t\t\treturn client.getTimeMSToNextTick();\n\t\t\t}\n\t\t}\n\t\treturn -1;\n\t}\n\n\t@Override\n\tpublic boolean isTicking(final Tickable E, final int tickID)\n\t{\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\tfinal TickableGroup almostTock=e.next();\n\t\t\tfinal Iterator<TickClient> set=almostTock.getTickSet(E,tickID);\n\t\t\tif(set.hasNext())\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isAllSuspended()\n\t{\n\t\treturn isSuspended;\n\t}\n\n\t@Override\n\tpublic void suspendAll(final CMRunnable[] exceptRs)\n\t{\n\t\tunsuspendedRunnables = exceptRs;\n\t\tisSuspended = true;\n\t}\n\n\t@Override\n\tpublic void resumeAll()\n\t{\n\t\tunsuspendedRunnables = null;\n\t\tisSuspended = false;\n\t}\n\n\t@Override\n\tpublic void suspendTicking(final Tickable E, final int tickID)\n\t{\n\t\tsuspendResumeTicking(E, tickID, true);\n\t}\n\n\t@Override\n\tpublic void resumeTicking(final Tickable E, final int tickID)\n\t{\n\t\tsuspendResumeTicking(E, tickID, false);\n\t}\n\n\tprotected boolean suspendResumeTicking(final Tickable E, final int tickID, final boolean suspend)\n\t{\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\tfinal TickableGroup almostTock=e.next();\n\t\t\tfor(final Iterator<TickClient> set=almostTock.getTickSet(E,tickID);set.hasNext();)\n\t\t\t\tset.next().setSuspended(suspend);\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean isSuspended(final Tickable E, final int tickID)\n\t{\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\tfinal TickableGroup almostTock=e.next();\n\t\t\tfinal Iterator<TickClient> set=almostTock.getTickSet(E,tickID);\n\t\t\tif(set.hasNext() && set.next().isSuspended())\n\t\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic String getSystemReport(final String itemCode)\n\t{\n\t\tfinal String cd=itemCode.toLowerCase();\n\t\tif(cd.startsWith("topmob")||cd.startsWith("totalmob"))\n\t\t{\n\t\t\tlong totalMOBMillis=0;\n\t\t\tlong totalMOBTicks=0;\n\t\t\tlong topMOBMillis=0;\n\t\t\tlong topMOBTicks=0;\n\t\t\tMOB topMOBClient=null;\n\t\t\tfor(final Session S : CMLib.sessions().localOnlineIterable())\n\t\t\t{\n\t\t\t\ttotalMOBMillis+=S.getTotalMillis();\n\t\t\t\ttotalMOBTicks+=S.getTotalTicks();\n\t\t\t\tif(S.getTotalMillis()>topMOBMillis)\n\t\t\t\t{\n\t\t\t\t\ttopMOBMillis=S.getTotalMillis();\n\t\t\t\t\ttopMOBTicks=S.getTotalTicks();\n\t\t\t\t\ttopMOBClient=S.mob();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif(itemCode.equalsIgnoreCase("totalMOBMillis"))\n\t\t\t\treturn ""+totalMOBMillis;\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("totalMOBMillisTime"))\n\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(totalMOBMillis,0);\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("totalMOBMillisTimePlusAverage"))\n\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(totalMOBMillis,totalMOBTicks);\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("totalMOBTicks"))\n\t\t\t\treturn ""+totalMOBTicks;\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("topMOBMillis"))\n\t\t\t\treturn ""+topMOBMillis;\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("topMOBMillisTime"))\n\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(topMOBMillis,0);\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("topMOBMillisTimePlusAverage"))\n\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(topMOBMillis,topMOBTicks);\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("topMOBTicks"))\n\t\t\t\treturn ""+topMOBTicks;\n\t\t\telse\n\t\t\tif(itemCode.equalsIgnoreCase("topMOBClient"))\n\t\t\t{\n\t\t\t\tif(topMOBClient!=null)\n\t\t\t\t\treturn topMOBClient.Name();\n\t\t\t\treturn "";\n\t\t\t}\n\t\t\treturn "";\n\t\t}\n\t\telse\n\t\tif(itemCode.toLowerCase().startsWith("tickerproblems"))\n\t\t{\n\t\t\tfinal int x=itemCode.indexOf(\'-\');\n\t\t\tint total=10;\n\t\t\tif(x>0)\n\t\t\t\ttotal=CMath.s_int(itemCode.substring(x+1));\n\t\t\tfinal Vector<Triad<Long,Integer,Integer>> list=new Vector<Triad<Long,Integer,Integer>>();\n\t\t\tint group=0;\n\t\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t\t{\n\t\t\t\tfinal TickableGroup almostTock=e.next();\n\t\t\t\tint tick=0;\n\t\t\t\tfor(final Iterator<TickClient> et=almostTock.tickers();et.hasNext();)\n\t\t\t\t{\n\t\t\t\t\tfinal TickClient C=et.next();\n\t\t\t\t\tif(C.getTickTotal()==0)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tfinal Long avg=Long.valueOf(C.getMilliTotal()/C.getTickTotal());\n\t\t\t\t\tint i=0;\n\t\t\t\t\tfor(;i<list.size();i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal Triad<Long,Integer,Integer> t=list.get(i);\n\t\t\t\t\t\tif(avg.longValue()>=t.first.longValue())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlist.add(i,new Triad<Long,Integer,Integer>(avg,Integer.valueOf(group),Integer.valueOf(tick)));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif((list.size()==0)||((i>=list.size())&&(list.size()<total)))\n\t\t\t\t\t\tlist.add(new Triad<Long,Integer,Integer>(avg,Integer.valueOf(group),Integer.valueOf(tick)));\n\t\t\t\t\twhile(list.size()>total)\n\t\t\t\t\t\tlist.remove(list.size()-1);\n\t\t\t\t\ttick++;\n\t\t\t\t}\n\t\t\t\tgroup++;\n\t\t\t}\n\t\t\tif(list.size()==0)\n\t\t\t\treturn "";\n\t\t\tfinal StringBuilder str=new StringBuilder("");\n\t\t\tfor(final Triad<Long,Integer,Integer> t : list)\n\t\t\t\tstr.append(\';\').append(t.second).append(\',\').append(t.third);\n\t\t\treturn str.toString().substring(1);\n\t\t}\n\t\telse\n\t\tif(itemCode.toLowerCase().startsWith("tickerprob2"))\n\t\t{\n\t\t\tfinal int x=itemCode.indexOf(\'-\');\n\t\t\tint total=10;\n\t\t\tif(x>0)\n\t\t\t\ttotal=CMath.s_int(itemCode.substring(x+1));\n\t\t\tfinal Vector<Quad<Long,Integer,Integer,String>> list=new Vector<Quad<Long,Integer,Integer,String>>();\n\t\t\tint group=0;\n\t\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t\t{\n\t\t\t\tfinal TickableGroup almostTock=e.next();\n\t\t\t\tint tick=0;\n\t\t\t\tfor(final Iterator<TickClient> et=almostTock.tickers();et.hasNext();)\n\t\t\t\t{\n\t\t\t\t\tfinal TickClient C=et.next();\n\t\t\t\t\tif(C.getTickTotal()==0)\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tint i=0;\n\t\t\t\t\tfor(;i<list.size();i++)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal Triad<Long,Integer,Integer> t=list.get(i);\n\t\t\t\t\t\tif(C.getMilliTotal()>=t.first.longValue())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tlist.add(i,new Quad<Long,Integer,Integer,String>(Long.valueOf(C.getMilliTotal()),Integer.valueOf(group),Integer.valueOf(tick),C.getClientObject().name()));\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif((list.size()==0)||((i>=list.size())&&(list.size()<total)))\n\t\t\t\t\t\tlist.add(new Quad<Long,Integer,Integer,String>(Long.valueOf(C.getMilliTotal()),Integer.valueOf(group),Integer.valueOf(tick),C.getClientObject().name()));\n\t\t\t\t\twhile(list.size()>total)\n\t\t\t\t\t\tlist.remove(list.size()-1);\n\t\t\t\t\ttick++;\n\t\t\t\t}\n\t\t\t\tgroup++;\n\t\t\t}\n\t\t\tif(list.size()==0)\n\t\t\t\treturn "";\n\t\t\tfinal StringBuilder str=new StringBuilder("");\n\t\t\tfor(final Quad<Long,Integer,Integer,String> t : list)\n\t\t\t\tstr.append(\';\').append(t.second).append(\',\').append(t.third);\n\t\t\treturn str.toString().substring(1);\n\t\t}\n\n\t\tint totalTickers=0;\n\t\tlong totalMillis=0;\n\t\tlong totalTicks=0;\n\t\tint topGroupNumber=-1;\n\t\tlong topGroupMillis=-1;\n\t\tlong topGroupTicks=0;\n\t\tlong topObjectMillis=-1;\n\t\tlong topObjectTicks=0;\n\t\tint topObjectGroup=0;\n\t\tTickable topObjectClient=null;\n\t\tint num=0;\n\t\tTickableGroup almostTock = null;\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\talmostTock=e.next();\n\t\t\ttotalTickers+=almostTock.numTickers();\n\t\t\ttotalMillis+=almostTock.getMilliTotal();\n\t\t\ttotalTicks+=almostTock.getTickTotal();\n\t\t\tif(almostTock.getMilliTotal()>topGroupMillis)\n\t\t\t{\n\t\t\t\ttopGroupMillis=almostTock.getMilliTotal();\n\t\t\t\ttopGroupTicks=almostTock.getTickTotal();\n\t\t\t\ttopGroupNumber=num;\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfor(final Iterator<TickClient> et=almostTock.tickers();et.hasNext();)\n\t\t\t\t{\n\t\t\t\t\tfinal TickClient C=et.next();\n\t\t\t\t\tif(C.getMilliTotal()>topObjectMillis)\n\t\t\t\t\t{\n\t\t\t\t\t\ttopObjectMillis=C.getMilliTotal();\n\t\t\t\t\t\ttopObjectTicks=C.getTickTotal();\n\t\t\t\t\t\ttopObjectClient=C.getClientObject();\n\t\t\t\t\t\ttopObjectGroup=num;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch(final NoSuchElementException ex)\n\t\t\t{\n\t\t\t}\n\t\t\tnum++;\n\t\t}\n\t\tif(itemCode.equalsIgnoreCase("freeMemory"))\n\t\t\treturn ""+(Runtime.getRuntime().freeMemory()/1000);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("totalMemory"))\n\t\t\treturn ""+(Runtime.getRuntime().totalMemory()/1000);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("totalTime"))\n\t\t\treturn ""+CMLib.english().stringifyElapsedTimeOrTicks(System.currentTimeMillis()-CMSecurity.getStartTime(),0);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("startTime"))\n\t\t\treturn CMLib.time().date2String(CMSecurity.getStartTime());\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("currentTime"))\n\t\t\treturn CMLib.time().date2String(System.currentTimeMillis());\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("totalTickers"))\n\t\t\treturn ""+totalTickers;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("totalMillis"))\n\t\t\treturn ""+totalMillis;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("totalMillisTime"))\n\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(totalMillis,0);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("totalMillisTimePlusAverage"))\n\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(totalMillis,totalTicks);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("totalTicks"))\n\t\t\treturn ""+totalTicks;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("tickgroupsize"))\n\t\t\treturn ""+allTicks.size();\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("numthreads"))\n\t\t\treturn ""+getPoolExecutor(null).getPoolSize();\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("numactivethreads"))\n\t\t\treturn ""+getPoolExecutor(null).getActiveCount();\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topGroupNumber"))\n\t\t\treturn ""+topGroupNumber;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topGroupMillis"))\n\t\t\treturn ""+topGroupMillis;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topGroupMillisTime"))\n\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(topGroupMillis,0);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topGroupMillisTimePlusAverage"))\n\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(topGroupMillis,topGroupTicks);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topGroupTicks"))\n\t\t\treturn ""+topGroupTicks;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topObjectMillis"))\n\t\t\treturn ""+topObjectMillis;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topObjectMillisTime"))\n\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(topObjectMillis,0);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topObjectMillisTimePlusAverage"))\n\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(topObjectMillis,topObjectTicks);\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topObjectTicks"))\n\t\t\treturn ""+topObjectTicks;\n\t\telse\n\t\tif(itemCode.equalsIgnoreCase("topObjectGroup"))\n\t\t\treturn ""+topObjectGroup;\n\t\telse\n\t\tif(itemCode.toLowerCase().startsWith("thread"))\n\t\t{\n\t\t\tfinal int xstart="thread".length();\n\t\t\tint xend=xstart;\n\t\t\twhile((xend<itemCode.length())&&(Character.isDigit(itemCode.charAt(xend))))\n\t\t\t\txend++;\n\t\t\tfinal int threadNum=CMath.s_int(itemCode.substring(xstart,xend));\n\t\t\tint curThreadNum=0;\n\t\t\tfor(final Enumeration<CMLibrary> e=CMLib.libraries();e.hasMoreElements();)\n\t\t\t{\n\t\t\t\tfinal CMLibrary lib=e.nextElement();\n\t\t\t\tfinal TickClient serviceClient=lib.getServiceClient();\n\t\t\t\tif(serviceClient!=null)\n\t\t\t\t{\n\t\t\t\t\tif(curThreadNum==threadNum)\n\t\t\t\t\t{\n\t\t\t\t\t\tfinal String instrCode=itemCode.substring(xend);\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("activeMiliTotal"))\n\t\t\t\t\t\t\treturn ""+serviceClient.getMilliTotal();\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("milliTotal"))\n\t\t\t\t\t\t\treturn ""+serviceClient.getMilliTotal();\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("status"))\n\t\t\t\t\t\t\treturn ""+serviceClient.getStatus();\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("name"))\n\t\t\t\t\t\t\treturn ""+serviceClient.getName();\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("MilliTotalTime"))\n\t\t\t\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(serviceClient.getMilliTotal(),0);\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("MiliTotalTime"))\n\t\t\t\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(serviceClient.getMilliTotal(),0);\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("MilliTotalTimePlusAverage")||instrCode.equalsIgnoreCase("MiliTotalTimePlusAverage"))\n\t\t\t\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(serviceClient.getMilliTotal(),serviceClient.getTickTotal());\n\t\t\t\t\t\tif(instrCode.equalsIgnoreCase("TickTotal"))\n\t\t\t\t\t\t\treturn ""+serviceClient.getTickTotal();\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcurThreadNum++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\tif(itemCode.equalsIgnoreCase("topObjectClient"))\n\t\t{\n\t\t\tif(topObjectClient!=null)\n\t\t\t\treturn topObjectClient.name();\n\t\t\treturn "";\n\t\t}\n\n\t\treturn "";\n\t}\n\n\t@Override\n\tpublic void rejuv(final Room here, final int tickID)\n\t{\n\t\tTickableGroup almostTock=null;\n\t\tTickClient C=null;\n\t\tTickable E2=null;\n\t\tfinal boolean doItems=((tickID<=0)||(tickID==Tickable.TICKID_ROOM_ITEM_REJUV));\n\t\tfinal boolean doMobs=((tickID<=0)||(tickID==Tickable.TICKID_MOB));\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\talmostTock=e.next();\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfor(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)\n\t\t\t\t{\n\t\t\t\t\tC=i.next();\n\t\t\t\t\tE2=C.getClientObject();\n\t\t\t\t\tif((doItems)\n\t\t\t\t\t&&(E2 instanceof ItemTicker)\n\t\t\t\t\t&&((here==null)||(((ItemTicker)E2).properLocation()==here)))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(C.tickTicker(true))\n\t\t\t\t\t\t\talmostTock.delTicker(C);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\tif((doMobs)\n\t\t\t\t\t&&(E2 instanceof MOB)\n\t\t\t\t\t&&(((MOB)E2).amDead())\n\t\t\t\t\t&&((here==null)||(((MOB)E2).getStartRoom()==here))\n\t\t\t\t\t&&(((MOB)E2).basePhyStats().rejuv()>0)\n\t\t\t\t\t&&(((MOB)E2).basePhyStats().rejuv()!=PhyStats.NO_REJUV)\n\t\t\t\t\t&&(((MOB)E2).phyStats().rejuv()>0))\n\t\t\t\t\t{\n\t\t\t\t\t\t((MOB)E2).phyStats().setRejuv(-1);\n\t\t\t\t\t\tif(C.tickTicker(true))\n\t\t\t\t\t\t\talmostTock.delTicker(C);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch(final NoSuchElementException ex)\n\t\t\t{\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<TickClient> findTickClient(final String name, final boolean exactOnly)\n\t{\n\t\tfinal List<TickClient> clientsV=new Vector<TickClient>(1);\n\t\tTickableGroup almostTock=null;\n\t\tTickClient C=null;\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\talmostTock=e.next();\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfor(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)\n\t\t\t\t{\n\t\t\t\t\tC=i.next();\n\t\t\t\t\tfinal Tickable T=(C!=null)?C.getClientObject():null;\n\t\t\t\t\tif(T!=null)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(exactOnly)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(T.name().equalsIgnoreCase(name))\n\t\t\t\t\t\t\t\tclientsV.add(C);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\tif(CMLib.english().containsString(T.name(), name))\n\t\t\t\t\t\t\tclientsV.add(C);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch(final NoSuchElementException ex)\n\t\t\t{\n\t\t\t}\n\t\t}\n\t\treturn clientsV;\n\t}\n\n\t@Override\n\tpublic void tickAllTickers(final Room here)\n\t{\n\t\tTickableGroup almostTock=null;\n\t\tTickClient C=null;\n\t\tTickable E2=null;\n\t\tfinal WorldMap map=CMLib.map();\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\talmostTock=e.next();\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfor(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)\n\t\t\t\t{\n\t\t\t\t\tC=i.next();\n\t\t\t\t\tE2=C.getClientObject();\n\t\t\t\t\tif(here==null)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(C.tickTicker(false))\n\t\t\t\t\t\t\talmostTock.delTicker(C);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\tif(map.isHere(E2,here))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(C.tickTicker(false))\n\t\t\t\t\t\t\talmostTock.delTicker(C);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\tif((E2 instanceof Ability)\n\t\t\t\t\t&&(map.isHere(((Ability)E2).affecting(),here)))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(C.tickTicker(false))\n\t\t\t\t\t\t\talmostTock.delTicker(C);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch(final NoSuchElementException ex)\n\t\t\t{\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void suspendResumeRecurse(final CMObject O, final boolean skipEmbeddedAreas, final boolean suspend)\n\t{\n\t\tif(O instanceof Item)\n\t\t{\n\t\t\tfinal Item I=(Item)O;\n\t\t\tsuspendResumeTicking(I, -1, suspend);\n\t\t\tif((I instanceof Boardable)&&(!skipEmbeddedAreas))\n\t\t\t\tsuspendResumeRecurse(((Boardable)I).getArea(),true,suspend);\n\t\t}\n\t\telse\n\t\tif(O instanceof MOB)\n\t\t{\n\t\t\tfinal MOB M=(MOB)O;\n\t\t\tfor(int i=0;i<M.numItems();i++)\n\t\t\t{\n\t\t\t\tfinal Item I=M.getItem(i);\n\t\t\t\tsuspendResumeRecurse(I,skipEmbeddedAreas,suspend);\n\t\t\t}\n\t\t\tfinal PlayerStats pStats=M.playerStats();\n\t\t\tif(pStats!=null)\n\t\t\t{\n\t\t\t\tfinal ItemCollection collection=pStats.getExtItems();\n\t\t\t\tif(collection!=null)\n\t\t\t\tfor(int i=0;i<collection.numItems();i++)\n\t\t\t\t{\n\t\t\t\t\tfinal Item I=collection.getItem(i);\n\t\t\t\t\tsuspendResumeRecurse(I,skipEmbeddedAreas,suspend);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// dont suspend players -- they are handled differently\n\t\t\t\tsuspendResumeTicking(M, -1, suspend);\n\t\t\t}\n\t\t}\n\t\telse\n\t\tif(O instanceof Room)\n\t\t{\n\t\t\tfinal Room R=(Room)O;\n\t\t\tsuspendResumeTicking(R, -1, suspend);\n\t\t\tfor(int i=0;i<R.numInhabitants();i++)\n\t\t\t\tsuspendResumeRecurse(R.fetchInhabitant(i),skipEmbeddedAreas,suspend);\n\t\t\tfor(int i=0;i<R.numItems();i++)\n\t\t\t\tsuspendResumeRecurse(R.getItem(i),skipEmbeddedAreas,suspend);\n\t\t}\n\t\telse\n\t\tif(O instanceof Area)\n\t\t{\n\t\t\tfinal Area A=(Area)O;\n\t\t\tA.setAreaState(suspend?Area.State.FROZEN:Area.State.ACTIVE);\n\t\t\tfor(final Enumeration<Room> r=A.getFilledProperMap();r.hasMoreElements();)\n\t\t\t\tsuspendResumeRecurse(r.nextElement(),skipEmbeddedAreas,suspend);\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean deleteAllTicks(final Tickable ticker)\n\t{\n\t\tif(ticker==null)\n\t\t\treturn false;\n\t\tdeleteTick(ticker, -1);\n\t\tfinal WorldMap map=CMLib.map();\n\t\tboolean deleted = false;\n\t\tif(ticker instanceof MOB)\n\t\t{\n\t\t\tfinal MOB M=(MOB)ticker;\n\t\t\tfor(int i=0;i<M.numItems();i++)\n\t\t\t\tdeleteTick(M.getItem(i), -1);\n\t\t}\n\t\tif((ticker instanceof Physical)\n\t\t&&(!(ticker instanceof MOB)))\n\t\t{\n\t\t\tfinal Physical P=(Physical)ticker;\n\t\t\tfor(int i=0;i<P.numEffects();i++)\n\t\t\t\tdeleteTick(P.fetchEffect(i), -1);\n\t\t}\n\t\tif(ticker instanceof Room)\n\t\t{\n\t\t\tTickableGroup almostTock=null;\n\t\t\tTickClient C=null;\n\t\t\tTickable E2=null;\n\t\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t\t{\n\t\t\t\talmostTock=e.next();\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tfor(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)\n\t\t\t\t\t{\n\t\t\t\t\t\tC=i.next();\n\t\t\t\t\t\tE2=C.getClientObject();\n\t\t\t\t\t\tif(map.isHere(E2,(Room)ticker))\n\t\t\t\t\t\t\tdeleted = almostTock.delTicker(C) || deleted;\n\t\t\t\t\t\telse\n\t\t\t\t\t\tif((E2 instanceof Ability)\n\t\t\t\t\t\t&&(map.isHere(((Ability)E2).affecting(),(Room)ticker)))\n\t\t\t\t\t\t\tdeleted = almostTock.delTicker(C) || deleted;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\ttry\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(almostTock.numTickers()==0)\n\t\t\t\t\t\t\t\tallTicks.remove(almostTock);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final Exception e2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch(final NoSuchElementException ex)\n\t\t\t\t{\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse\n\t\tif(ticker instanceof Area)\n\t\t{\n\t\t\tTickableGroup almostTock=null;\n\t\t\tTickClient C=null;\n\t\t\tTickable E2=null;\n\t\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t\t{\n\t\t\t\talmostTock=e.next();\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tfor(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)\n\t\t\t\t\t{\n\t\t\t\t\t\tC=i.next();\n\t\t\t\t\t\tE2=C.getClientObject();\n\t\t\t\t\t\tif(map.isHere(E2,(Area)ticker))\n\t\t\t\t\t\t\tdeleted = almostTock.delTicker(C) || deleted;\n\t\t\t\t\t\telse\n\t\t\t\t\t\tif((E2 instanceof Ability)\n\t\t\t\t\t\t&&(map.isHere(((Ability)E2).affecting(),(Area)ticker)))\n\t\t\t\t\t\t\tdeleted = almostTock.delTicker(C) || deleted;\n\t\t\t\t\t\telse\n\t\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\t\ttry\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif(almostTock.numTickers()==0)\n\t\t\t\t\t\t\t\tallTicks.remove(almostTock);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (final Exception e2)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcatch(final NoSuchElementException ex)\n\t\t\t\t{\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn deleted;\n\t}\n\n\t@Override\n\tpublic String getTickInfoReport(final String which)\n\t{\n\t\tint grpstart=-1;\n\t\tfor(int i=0;i<which.length();i++)\n\t\t{\n\t\t\tif(Character.isDigit(which.charAt(i)))\n\t\t\t{\n\t\t\t\tgrpstart=i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif(which.equalsIgnoreCase("tickGroupSize"))\n\t\t\treturn ""+allTicks.size();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerssize"))\n\t\t{\n\t\t\tif(grpstart<0)\n\t\t\t\treturn"";\n\t\t\tfinal int group=CMath.s_int(which.substring(grpstart));\n\t\t\tif((group>=0)&&(group<allTicks.size()))\n\t\t\t{\n\t\t\t\tfinal List<TickableGroup> enumeratedTicks=new XVector<TickableGroup>(allTicks);\n\t\t\t\treturn ""+enumeratedTicks.get(group).numTickers();\n\t\t\t}\n\t\t\treturn "";\n\t\t}\n\t\tint group=-1;\n\t\tint client=-1;\n\t\tfinal int clistart=which.indexOf(\'-\');\n\t\tif((grpstart>=0)&&(clistart>grpstart))\n\t\t{\n\t\t\tgroup=CMath.s_int(which.substring(grpstart,clistart));\n\t\t\tclient=CMath.s_int(which.substring(clistart+1));\n\t\t}\n\n\t\tif((group<0)||(client<0)||(group>=allTicks.size()))\n\t\t\treturn "";\n\t\tfinal List<TickableGroup> enumeratedTicks=new XVector<TickableGroup>(allTicks);\n\t\tif((group<0)||(client<0)||(group>=enumeratedTicks.size()))\n\t\t\treturn "";\n\t\tfinal TickableGroup almostTock=enumeratedTicks.get(group);\n\n\t\tif(client>=almostTock.numTickers())\n\t\t\treturn "";\n\t\tfinal TickClient C=almostTock.fetchTickerByIndex(client);\n\t\tif(C==null)\n\t\t\treturn "";\n\n\t\tif(which.toLowerCase().startsWith("tickername"))\n\t\t{\n\t\t\tTickable E=C.getClientObject();\n\t\t\tif((E instanceof Ability)&&(E.ID().equals("ItemRejuv")))\n\t\t\t\tE=((Ability)E).affecting();\n\t\t\tif(E instanceof Room)\n\t\t\t\treturn CMLib.map().getExtendedRoomID((Room)E);\n\t\t\tif(E!=null)\n\t\t\t\treturn E.name();\n\t\t\treturn "!NULL!";\n\t\t}\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerid"))\n\t\t\treturn ""+C.getTickID();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerstatusstr"))\n\t\t\treturn C.getStatus();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerstatus"))\n\t\t\treturn ((C.getClientObject()==null)?"":(""+C.getClientObject().getTickStatus()));\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickercodeword"))\n\t\t\treturn getTickStatusSummary(C.getClientObject());\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickertickdown"))\n\t\t\treturn ""+C.getCurrentTickDown();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerretickdown"))\n\t\t\treturn ""+C.getTotalTickDown();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickermillitotal"))\n\t\t\treturn ""+C.getMilliTotal();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickermilliavg"))\n\t\t{\n\t\t\tif(C.getTickTotal()==0)\n\t\t\t\treturn "0";\n\t\t\treturn ""+(C.getMilliTotal()/C.getTickTotal());\n\t\t}\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerlaststartmillis"))\n\t\t\treturn ""+C.getLastStartTime();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerlaststopmillis"))\n\t\t\treturn ""+C.getLastStopTime();\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerlaststartdate"))\n\t\t\treturn CMLib.time().date2String(C.getLastStartTime());\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerlaststopdate"))\n\t\t\treturn CMLib.time().date2String(C.getLastStopTime());\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickerlastduration"))\n\t\t{\n\t\t\tif(C.getLastStopTime()>=C.getLastStartTime())\n\t\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(C.getLastStopTime()-C.getLastStartTime(),0);\n\t\t\treturn CMLib.english().stringifyElapsedTimeOrTicks(System.currentTimeMillis()-C.getLastStartTime(),0);\n\t\t}\n\t\telse\n\t\tif(which.toLowerCase().startsWith("tickersuspended"))\n\t\t\treturn ""+C.isSuspended();\n\t\treturn "";\n\t}\n\n\t@Override\n\tpublic boolean shutdown()\n\t{\n\t\t//int numTicks=tickGroup.size();\n\t\twhile(allTicks.size()>0)\n\t\t{\n\t\t\t//Log.sysOut("ServiceEngine","Shutting down all tick "+which+"/"+numTicks+"...");\n\t\t\tfinal TickableGroup tock=allTicks.iterator().next();\n\t\t\tif(tock!=null)\n\t\t\t{\n\t\t\t\tCMProps.setUpAllLowVar(CMProps.Str.MUDSTATUS,"Shutting down...shutting down Service Engine: killing "+tock.getName()+": "+tock.getStatus());\n\t\t\t\ttock.shutdown();\n\t\t\t\tallTicks.remove(tock);\n\t\t\t}\n\t\t\tCMLib.s_sleep(100);\n\t\t}\n\t\tCMProps.setUpAllLowVar(CMProps.Str.MUDSTATUS,"Shutting down...shutting down Service Engine: "+ID()+": thread shutdown");\n\t\tCMLib.killThread(drivingThread,100,10);\n\t\t// force final time tick!\n\t\tfinal Vector<TimeClock> timeObjects=new Vector<TimeClock>();\n\t\tfor(final Enumeration<Area> e=CMLib.map().areas();e.hasMoreElements();)\n\t\t{\n\t\t\tfinal Area A=(e.nextElement());\n\t\t\tif(!timeObjects.contains(A.getTimeObj()))\n\t\t\t\ttimeObjects.addElement(A.getTimeObj());\n\t\t}\n\t\tCMProps.setUpAllLowVar(CMProps.Str.MUDSTATUS,"Shutting down...shutting down Service Engine: "+ID()+": saving time objects");\n\t\tfor(int t=0;t<timeObjects.size();t++)\n\t\t\ttimeObjects.elementAt(t).save();\n\t\tfor(final CMThreadPoolExecutor pool : threadPools)\n\t\t{\n\t\t\tif(pool != null)\n\t\t\t{\n\t\t\t\tpool.shutdown();\n\t\t\t\ttry\n\t\t\t\t{\n\t\t\t\t\tpool.awaitTermination(2, TimeUnit.SECONDS);\n\t\t\t\t}\n\t\t\t\tcatch (final InterruptedException e)\n\t\t\t\t{\n\t\t\t\t\tpool.shutdownNow();\n\t\t\t\t\ttry\n\t\t\t\t\t{\n\t\t\t\t\t\tpool.awaitTermination(3, TimeUnit.SECONDS);\n\t\t\t\t\t}\n\t\t\t\t\tcatch (final InterruptedException e2)\n\t\t\t\t\t{\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tLog.sysOut("ServiceEngine","Shutdown complete.");\n\t\treturn true;\n\t}\n\n\t@Override\n\tpublic synchronized void clearDebri(final Room room, final int taskCode)\n\t{\n\t\tTickableGroup almostTock=null;\n\t\tTickClient C=null;\n\t\tItemTicker  I=null;\n\t\tIterator<TickClient> roomSet;\n\t\tMOB mob=null;\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\talmostTock=e.next();\n\t\t\troomSet=almostTock.getLocalItems(taskCode,room);\n\t\t\tif(roomSet!=null)\n\t\t\t{\n\t\t\t\tfor(;roomSet.hasNext();)\n\t\t\t\t{\n\t\t\t\t\tC=roomSet.next();\n\t\t\t\t\tif(C.getClientObject() instanceof ItemTicker)\n\t\t\t\t\t{\n\t\t\t\t\t\tI=(ItemTicker)C.getClientObject();\n\t\t\t\t\t\talmostTock.delTicker(C);\n\t\t\t\t\t\tI.setProperLocation(null);\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\tif(C.getClientObject() instanceof MOB)\n\t\t\t\t\t{\n\t\t\t\t\t\tmob=(MOB)C.getClientObject();\n\t\t\t\t\t\tif((mob.isMonster())\n\t\t\t\t\t\t&&(!room.isInhabitant(mob))\n\t\t\t\t\t\t&&((mob.amFollowing()==null)\n\t\t\t\t\t\t\t||(!mob.amUltimatelyFollowing().isPlayer())\n\t\t\t\t\t\t\t||(!CMLib.flags().isInTheGame(mob.amUltimatelyFollowing(), true))))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmob.destroy();\n\t\t\t\t\t\t\talmostTock.delTicker(C);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic List<Tickable> getNamedTickingObjects(String name)\n\t{\n\t\tfinal Vector<Tickable> V=new Vector<Tickable>();\n\t\tTickableGroup almostTock=null;\n\t\tTickClient C=null;\n\t\tname=name.toUpperCase().trim();\n\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t{\n\t\t\talmostTock=e.next();\n\t\t\tfor(final Iterator<TickClient> i=almostTock.tickers();i.hasNext();)\n\t\t\t{\n\t\t\t\tC=i.next();\n\t\t\t\tif((C.getClientObject()!=null)\n\t\t\t\t&&(C.getClientObject().name().toUpperCase().indexOf(name)>=0)\n\t\t\t\t&&(!V.contains(C.getClientObject())))\n\t\t\t\t\tV.addElement(C.getClientObject());\n\t\t\t}\n\t\t}\n\t\treturn V;\n\t}\n\n\t@Override\n\tpublic String getTickStatusSummary(final Tickable obj)\n\t{\n\t\tif(obj==null)\n\t\t\treturn "";\n\t\tfinal long code=obj.getTickStatus();\n\t\tif(obj instanceof Environmental)\n\t\t{\n\t\t\tif(CMath.bset(code,Tickable.STATUS_BEHAVIOR) && (obj instanceof PhysicalAgent))\n\t\t\t{\n\t\t\t\tfinal long b=(code-Tickable.STATUS_BEHAVIOR);\n\t\t\t\tString codeWord="Behavior #"+b;\n\t\t\t\tif((b>=0)&&(b<((PhysicalAgent)obj).numBehaviors()))\n\t\t\t\t{\n\t\t\t\t\tfinal Behavior B=((PhysicalAgent)obj).fetchBehavior((int)b);\n\t\t\t\t\tcodeWord+=" ("+B.name()+": "+B.getTickStatus();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tcodeWord+=" (#Error#)";\n\t\t\t\treturn codeWord;\n\t\t\t}\n\t\t\telse\n\t\t\tif(CMath.bset(code,Tickable.STATUS_SCRIPT)&&(obj instanceof MOB))\n\t\t\t{\n\t\t\t\tfinal long b=(code-Tickable.STATUS_SCRIPT);\n\t\t\t\tString codeWord="Script #"+b;\n\t\t\t\tif((b>=0)&&(b<((MOB)obj).numScripts()))\n\t\t\t\t{\n\t\t\t\t\tfinal ScriptingEngine S=((MOB)obj).fetchScript((int)b);\n\t\t\t\t\tcodeWord+=" ("+CMStrings.limit(S.getScript(),20)+": "+S.getTickStatus();\n\t\t\t\t}\n\t\t\t\treturn codeWord;\n\t\t\t}\n\t\t\telse\n\t\t\tif((code&STATUS_ALLMISCTICKS)>0)\n\t\t\t{\n\t\t\t\tfinal long base=(code&STATUS_ALLMISCTICKS);\n\t\t\t\tint num=0;\n\t\t\t\tfor(int i=1;i<6;i++)\n\t\t\t\t{\n\t\t\t\t\tif((1<<(10+i))==base)\n\t\t\t\t\t{\n\t\t\t\t\t\tnum = i;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn "Misc"+num+" Activity #"+(code-base);\n\t\t\t}\n\t\t\telse\n\t\t\tif(CMath.bset(code,Tickable.STATUS_AFFECT) && (obj instanceof Physical))\n\t\t\t{\n\t\t\t\tfinal long b=(code-Tickable.STATUS_AFFECT);\n\t\t\t\tString codeWord="Effect #"+b;\n\t\t\t\tif((b>=0)&&(b<((Physical)obj).numEffects()))\n\t\t\t\t{\n\t\t\t\t\tfinal Environmental E=((Physical)obj).fetchEffect((int)b);\n\t\t\t\t\tcodeWord+=" ("+E.name()+": "+E.getTickStatus()+")";\n\t\t\t\t}\n\t\t\t\treturn codeWord;\n\t\t\t}\n\t\t}\n\t\tString codeWord=null;\n\t\tif(CMath.bset(code,Tickable.STATUS_BEHAVIOR))\n\t\t\tcodeWord="Behavior?!";\n\t\telse\n\t\tif(CMath.bset(code,Tickable.STATUS_SCRIPT))\n\t\t\tcodeWord="Script?!";\n\t\t else\n\t\tif(CMath.bset(code,Tickable.STATUS_AFFECT))\n\t\t\tcodeWord="Effect?!";\n\t\telse\n\t\tswitch((int)code)\n\t\t{\n\t\tcase Tickable.STATUS_ALIVE:\n\t\t\tcodeWord="Alive"; break;\n\t\tcase Tickable.STATUS_REBIRTH:\n\t\t\tcodeWord="Rebirth"; break;\n\t\tcase Tickable.STATUS_CLASS:\n\t\t\tcodeWord="Class"; break;\n\t\tcase Tickable.STATUS_DEAD:\n\t\t\tcodeWord="Dead"; break;\n\t\tcase Tickable.STATUS_END:\n\t\t\tcodeWord="End"; break;\n\t\tcase Tickable.STATUS_FIGHT:\n\t\t\tcodeWord="Fighting"; break;\n\t\tcase Tickable.STATUS_NOT:\n\t\t\tcodeWord="!"; break;\n\t\tcase Tickable.STATUS_OTHER:\n\t\t\tcodeWord="Other"; break;\n\t\tcase Tickable.STATUS_RACE:\n\t\t\tcodeWord="Race"; break;\n\t\tcase Tickable.STATUS_START:\n\t\t\tcodeWord="Start"; break;\n\t\tcase Tickable.STATUS_WEATHER:\n\t\t\tcodeWord="Weather"; break;\n\t\tdefault:\n\t\t\tcodeWord="?"; break;\n\t\t}\n\t\treturn codeWord;\n\t}\n\n\tpublic void insertOrderDeathInOrder(final DVector DV, final long lastStart, final String msg, final TickableGroup tock)\n\t{\n\t\tif(DV.size()>0)\n\t\tfor(int i=0;i<DV.size();i++)\n\t\t{\n\t\t\tif(((Long)DV.elementAt(i,1)).longValue()>lastStart)\n\t\t\t{\n\t\t\t\tDV.insertElementAt(i,Long.valueOf(lastStart),msg,tock);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tDV.addElement(Long.valueOf(lastStart),msg,tock);\n\t}\n\n\tpublic void setSupportStatus(final String s)\n\t{\n\t\tif(supportClient != null)\n\t\t{\n\t\t\tsupportClient.setStatus(s);\n\t\t\tif(CMSecurity.isDebugging(CMSecurity.DbgFlag.UTILITHREAD))\n\t\t\t\tLog.debugOut("ServiceEngine",s);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void dumpDebugStack(final String ID, final Thread theThread)\n\t{\n\t\t// I wish Java had compiler directives.  Would be great to un-comment this for 1.5 JVMs\n\t\tfinal StringBuffer dump = new StringBuffer("");\n\t\tif(theThread == null)\n\t\t\tdump.append("NULL!!");\n\t\telse\n\t\t{\n\t\t\tfinal java.lang.StackTraceElement[] s=theThread.getStackTrace();\n\t\t\tfor (final StackTraceElement element : s)\n\t\t\t\tdump.append("\\n   "+element.getClassName()+": "+element.getMethodName()+"("+element.getFileName()+": "+element.getLineNumber()+")");\n\t\t}\n\t\tLog.debugOut(ID,dump.toString());\n\t}\n\n\tprotected static void debugDumpThreadGroup(final ThreadGroup tGroup, final StringBuilder lines)\n\t{\n\t\tfinal int ac = tGroup.activeCount();\n\t\tfinal int agc = tGroup.activeGroupCount();\n\t\tfinal Thread tArray[] = new Thread [ac+1];\n\t\tfinal ThreadGroup tgArray[] = new ThreadGroup [agc+1];\n\n\t\ttGroup.enumerate(tArray,false);\n\t\ttGroup.enumerate(tgArray,false);\n\n\t\tlines.append(" ^HTGRP^?  ^H" + tGroup.getName() + "^?\\n\\r");\n\t\tfor (int i = 0; i<ac; ++i)\n\t\t{\n\t\t\tif (tArray[i] != null)\n\t\t\t{\n\t\t\t\tlines.append(tArray[i].isAlive()? "  ok   " : " BAD!  ");\n\t\t\t\tlines.append(CMStrings.padRight(tArray[i].getName(),20)+": ");\n\t\t\t\tfinal String summary;\n\t\t\t\tif(tArray[i] instanceof MudHost)\n\t\t\t\t\tsummary=CMClass.classID(tArray[i])+": "+((MudHost)tArray[i]).getStatus();\n\t\t\t\telse\n\t\t\t\t\tsummary=tArray[i].toString();\n\t\t\t\tlines.append(summary+"\\n\\r");\n\t\t\t\tfinal java.lang.StackTraceElement[] s=tArray[i].getStackTrace();\n\t\t\t\tfor (final StackTraceElement element : s)\n\t\t\t\t\tlines.append("   "+element.getClassName()+": "+element.getMethodName()+"("+element.getFileName()+": "+element.getLineNumber()+")\\n\\r");\n\t\t\t}\n\t\t}\n\n\t\tif (agc > 0)\n\t\t{\n\t\t\tlines.append("{\\n\\r");\n\t\t\tfor (int i = 0; i<agc; ++i)\n\t\t\t{\n\t\t\t\tif (tgArray[i] != null)\n\t\t\t\t\tdebugDumpThreadGroup(tgArray[i],lines);\n\t\t\t}\n\t\t\tlines.append("}\\n\\r");\n\t\t}\n\t}\n\n\tpublic final void checkHealth()\n\t{\n\t\tfinal long lastDateTime=System.currentTimeMillis()-SHORT_TICK_TIMEOUT;\n\t\tfinal long longerDateTime=System.currentTimeMillis()-LONG_TICK_TIMEOUT;\n\t\tsetSupportStatus("checking");\n\n\t\tsetSupportStatus("checking tick groups.");\n\t\tfinal DVector orderedDeaths=new DVector(3);\n\t\ttry\n\t\t{\n\t\t\tTickableGroup almostTock = null;\n\t\t\tfor(final Iterator<TickableGroup> e=tickGroups();e.hasNext();)\n\t\t\t{\n\t\t\t\talmostTock=e.next();\n\t\t\t\tif((almostTock.isAwake())\n\t\t\t\t&&(almostTock.getLastStartTime()!=0)\n\t\t\t\t&&(almostTock.getLastStartTime()<lastDateTime))\n\t\t\t\t{\n\t\t\t\t\tfinal TickClient tickClient=almostTock.getLastTicked();\n\t\t\t\t\tfinal Tickable ticker=(tickClient!=null) ? tickClient.getClientObject() : null;\n\t\t\t\t\tif(tickClient==null)\n\t\t\t\t\t\tinsertOrderDeathInOrder(orderedDeaths,0,"LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+"! No further information.",almostTock);\n\t\t\t\t\telse\n\t\t\t\t\tif((!CMath.bset(tickClient.getTickID(),Tickable.TICKID_LONGERMASK))||(almostTock.getLastStopTime()<longerDateTime))\n\t\t\t\t\t{\n\t\t\t\t\t\tif(ticker==null)\n\t\t\t\t\t\t\tinsertOrderDeathInOrder(orderedDeaths,0,"LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+": NULL @"+CMLib.time().date2String(tickClient.getLastStartTime())+", tickID "+tickClient.getTickID(),almostTock);\n\t\t\t\t\t\telse\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tStringBuffer logError=null;\n\t\t\t\t\t\t\tfinal long code=ticker.getTickStatus();\n\t\t\t\t\t\t\tfinal String codeWord=getTickStatusSummary(ticker);\n\t\t\t\t\t\t\tString log=null;\n\t\t\t\t\t\t\tif(ticker instanceof Environmental)\n\t\t\t\t\t\t\t\tlogError=new StringBuffer("LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+": "+ticker.name()+" ("+((Environmental)ticker).ID()+") @"+CMLib.time().date2String(tickClient.getLastStartTime())+", status("+code+" ("+codeWord+"), tickID "+tickClient.getTickID());\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tlogError=new StringBuffer("LOCKED GROUP "+almostTock.getName()+": "+almostTock.getStatus()+": "+ticker.name()+", status ("+code+"/"+codeWord+") @"+CMLib.time().date2String(tickClient.getLastStartTime())+", tickID "+tickClient.getTickID());\n\n\t\t\t\t\t\t\tif((ticker instanceof MOB)&&(((MOB)ticker).location()!=null))\n\t\t\t\t\t\t\t\tlog=logError.toString()+" in "+((MOB)ticker).location().roomID();\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif((ticker instanceof Item)&&(((Item)ticker).owner()!=null)&&(((Item)ticker).owner() instanceof Room))\n\t\t\t\t\t\t\t\tlog=logError.toString()+" in "+((Room)((Item)ticker).owner()).roomID();\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif((ticker instanceof Item)&&(((Item)ticker).owner()!=null)&&(((Item)ticker).owner() instanceof MOB))\n\t\t\t\t\t\t\t\tlog=logError.toString()+" owned by "+((MOB)((Item)ticker).owner()).name();\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif(ticker instanceof Room)\n\t\t\t\t\t\t\t\tlog=logError.toString()+" is "+((Room)ticker).roomID();\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tlog=logError.toString();\n\t\t\t\t\t\t\tinsertOrderDeathInOrder(orderedDeaths,tickClient.getLastStartTime(),log,almostTock);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// no isDEBUGGING check -- just always let her rip.\n\t\t\t\t\tdumpDebugStack(Thread.currentThread().getName(),almostTock.getCurrentThread());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch (final java.util.NoSuchElementException e)\n\t\t{\n\t\t}\n\t\tfor(int i=0;i<orderedDeaths.size();i++)\n\t\t\tLog.errOut(Thread.currentThread().getName(),(String)orderedDeaths.elementAt(i,2));\n\n\t\tsetSupportStatus("killing tick groups.");\n\t\tfor(int x=0;x<orderedDeaths.size();x++)\n\t\t{\n\t\t\tfinal TickableGroup almostTock=(TickableGroup)orderedDeaths.elementAt(x,3);\n\t\t\tfinal Vector<TickClient> tockClients=new Vector<TickClient>();\n\t\t\ttry\n\t\t\t{\n\t\t\t\tfor(final Iterator<TickClient> e=almostTock.tickers();e.hasNext();)\n\t\t\t\t\ttockClients.addElement(e.next());\n\t\t\t}\n\t\t\tcatch (final NoSuchElementException e)\n\t\t\t{\n\t\t\t}\n\t\t\ttry\n\t\t\t{\n\t\t\t\talmostTock.shutdown();\n\t\t\t}\n\t\t\tcatch(final java.lang.ThreadDeath d)\n\t\t\t{\n\t\t\t\tLog.errOut("ThreadDeath killing "+almostTock.getName());\n\t\t\t}\n\t\t\tcatch(final Throwable t)\n\t\t\t{\n\t\t\t\tLog.errOut("Error "+t.getMessage()+" killing "+almostTock.getName());\n\t\t\t}\n\t\t\tif(CMLib.threads() instanceof ServiceEngine)\n\t\t\t\t((ServiceEngine)CMLib.threads()).delTickGroup(almostTock);\n\t\t\tfor(int i=0;i<tockClients.size();i++)\n\t\t\t{\n\t\t\t\tfinal TickClient c=tockClients.elementAt(i);\n\t\t\t\tstartTickDown(c.getClientObject(),c.getTickID(),c.getTotalTickDown());\n\t\t\t}\n\t\t}\n\n\t\tsetSupportStatus("Checking mud threads");\n\t\tfor(int m=0;m<CMLib.hosts().size();m++)\n\t\t{\n\t\t\tfinal List<Runnable> badThreads=CMLib.hosts().get(m).getOverdueThreads();\n\t\t\tfor(final Runnable T : badThreads)\n\t\t\t{\n\t\t\t\tif(T instanceof Thread)\n\t\t\t\t{\n\t\t\t\t\tString threadName=((Thread)T).getName();\n\t\t\t\t\tif(T instanceof Tickable)\n\t\t\t\t\t\tthreadName=((Tickable)T).name()+" ("+((Tickable)T).ID()+"): "+((Tickable)T).getTickStatus();\n\t\t\t\t\tsetSupportStatus("Killing "+threadName);\n\t\t\t\t\tLog.errOut("Killing stray thread: "+threadName);\n\t\t\t\t\tCMLib.killThread((Thread)T,100,1);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tLog.errOut("Unable to kill stray runnable: "+T.toString());\n\t\t\t}\n\t\t}\n\t\tsetSupportStatus("Done checking threads");\n\t}\n\n\t@Override\n\tpublic void run()\n\t{\n\t\twhile(!CMProps.getBoolVar(CMProps.Bool.MUDSTARTED))\n\t\t\tCMLib.s_sleep(1000);\n\t\tfinal CMProps props = CMProps.instance();\n\t\tfinal CMProps.Bool MUDSHUTTINGDOWN=CMProps.Bool.MUDSHUTTINGDOWN;\n\t\twhile(!props.getBool(MUDSHUTTINGDOWN))\n\t\t{\n\t\t\ttry\n\t\t\t{\n\t\t\t\twhile(isAllSuspended() && (!props.getBool(MUDSHUTTINGDOWN)))\n\t\t\t\t{\n\t\t\t\t\tif((unsuspendedRunnables!=null)&&(unsuspendedRunnables.length>0))\n\t\t\t\t\t{\n\t\t\t\t\t\tThread.sleep(100);\n\t\t\t\t\t\tfor(final CMRunnable runnable : unsuspendedRunnables)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttry\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\trunnable.run();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch(final Exception e)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tLog.errOut(e);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t\tThread.sleep(2000);\n\t\t\t\t}\n\t\t\t\tfinal long now=System.currentTimeMillis();\n\t\t\t\tglobalTickID = (now - globalStartTime) / props.tickMillis();\n\t\t\t\tlong nextWake=now + 3600000;\n\t\t\t\tif(this.schedTicks.size() > 0)\n\t\t\t\t{\n\t\t\t\t\tfinal List<CMRunnable> runThese = new LinkedList<CMRunnable>();\n\t\t\t\t\tsynchronized(this.schedTicks)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(this.schedTicks.size() > 0)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tfor(final Iterator<CMRunnable> r=this.schedTicks.iterator(); r.hasNext();)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tfinal CMRunnable R=r.next();\n\t\t\t\t\t\t\t\tif(now >= R.getStartTime())\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\trunThese.add(R);\n\t\t\t\t\t\t\t\t\tr.remove();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t\tif(R.getStartTime() < nextWake)\n\t\t\t\t\t\t\t\t\tnextWake = R.getStartTime();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor(final CMRunnable R : runThese)\n\t\t\t\t\t\tgetPoolExecutor((char)R.getGroupID()).execute(R);\n\t\t\t\t}\n\t\t\t\tsynchronized(allTicks)\n\t\t\t\t{\n\t\t\t\t\tfor(final TickableGroup T : allTicks)\n\t\t\t\t\t{\n\t\t\t\t\t\tif(!T.isAwake() && (!getPoolExecutor(T.getThreadGroupName()).isActiveOrQueued(T)))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tif (T.getNextTickTime() <= now)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tif(now + T.getTickInterval() < nextWake)\n\t\t\t\t\t\t\t\t\tnextWake = now + T.getTickInterval();\n\t\t\t\t\t\t\t\tgetPoolExecutor(T.getThreadGroupName()).execute(T);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\tif (nextWake > T.getNextTickTime())\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tnextWake = T.getNextTickTime();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse\n\t\t\t\t\t\tif(now + T.getTickInterval() < nextWake)\n\t\t\t\t\t\t\tnextWake = now + T.getTickInterval();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif(nextWake > now)\n\t\t\t\t{\n\t\t\t\t\tnextWakeAtTime = nextWake;\n\t\t\t\t\tThread.sleep(nextWake-now);\n\t\t\t\t}\n\t\t\t}\n\t\t\tcatch(final InterruptedException e)\n\t\t\t{\n\t\t\t}\n\t\t\tcatch(final Exception e)\n\t\t\t{\n\t\t\t\tLog.errOut("Scheduler",e);\n\t\t\t}\n\t\t}\n\t\tthreadPools=new CMThreadPoolExecutor[256];\n\t\tdrivingThread=null;\n\t\tsupportClient=null;\n\t\tLog.sysOut("ServiceEngine","Scheduler stopped");\n\t}\n\n\t@Override\n\tpublic boolean activate()\n\t{\n\t\tgetPoolExecutor(null); // will cause the creation\n\n\t\tif(supportClient==null)\n\t\tsupportClient=startTickDown(null,new Tickable()\n\t\t{\n\t\t\tprivate int tickStatus = Tickable.STATUS_NOT;\n\n\t\t\t\t@Override\n\t\t\t\tpublic String ID()\n\t\t\t\t{\n\t\t\t\t\treturn "THThreads";\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic CMObject newInstance()\n\t\t\t\t{\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic CMObject copyOf()\n\t\t\t\t{\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void initializeClass()\n\t\t\t\t{\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic int compareTo(final CMObject o)\n\t\t\t\t{\n\t\t\t\t\treturn (o == this) ? 0 : 1;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic String name()\n\t\t\t\t{\n\t\t\t\t\treturn ID();\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic int getTickStatus()\n\t\t\t\t{\n\t\t\t\t\treturn tickStatus;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic boolean tick(final Tickable ticking, final int tickID)\n\t\t\t\t{\n\t\t\t\tif((!CMSecurity.isDisabled(CMSecurity.DisFlag.UTILITHREAD))\n\t\t\t\t&&(!CMSecurity.isDisabled(CMSecurity.DisFlag.THREADTHREAD)))\n\t\t\t\t{\n\t\t\t\t\ttickStatus=Tickable.STATUS_ALIVE;\n\t\t\t\t\tcheckHealth();\n\t\t\t\t\tResources.removeResource("SYSTEM_HASHED_MASKS");\n\t\t\t\t\ttickStatus=Tickable.STATUS_NOT;\n\t\t\t\t\tsetSupportStatus("Sleeping");\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}, Tickable.TICKID_MISCELLANEOUS|Tickable.TICKID_SOLITARYMASK, MudHost.TIME_UTILTHREAD_SLEEP, 1);\n\t\tif(drivingThread==null)\n\t\t{\n\t\t\tdrivingThread=new Thread(Thread.currentThread().getThreadGroup(),this,"Scheduler"+Thread.currentThread().getThreadGroup().getName().charAt(0));\n\t\t\tdrivingThread.setDaemon(true);\n\t\t\tdrivingThread.start();\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static void panicDumpAllThreads()\n\t{\n\t\tThreadGroup topTG = Thread.currentThread().getThreadGroup();\n\t\twhile (topTG != null && topTG.getParent() != null)\n\t\t\ttopTG = topTG.getParent();\n\t\tfinal StringBuilder str=new StringBuilder("\\n\\r\\n\\rThread Dump:\\n\\r");\n\t\tif (topTG != null)\n\t\t{\n\t\t\tdebugDumpThreadGroup(topTG,str);\n\t\t}\n\t\tfinal PrintStream sysOut=System.out;\n\t\tfinal PrintStream errOut=System.out;\n\t\terrOut.println(str.toString());\n\t\tsysOut.println(str.toString());\n\t\tLog.errOut(str.toString());\n\t}\n}\n', 'message': 'group assign fix.\n\ngit-svn-id: 0cdf8356e41b2d8ccbb41bb76c82068fe80b2514@17453 0d6f1817-ed0e-0410-87c9-987e46238f29\n', 'author_name': 'bozimmerman', 'lang': 'java'}