package com.planet_ink.coffee_mud.core.intermud.i3.server; import com.planet_ink.coffee_mud.core.intermud.imc2.*; import com.planet_ink.coffee_mud.core.intermud.i3.packets.*; import com.planet_ink.coffee_mud.core.intermud.i3.persist.*; import com.planet_ink.coffee_mud.core.intermud.i3.server.*; import com.planet_ink.coffee_mud.core.intermud.i3.net.*; import com.planet_ink.coffee_mud.core.intermud.*; import com.planet_ink.coffee_mud.core.interfaces.*; import com.planet_ink.coffee_mud.core.*; import com.planet_ink.coffee_mud.core.CMSecurity.DisFlag; import com.planet_ink.coffee_mud.core.collections.*; import com.planet_ink.coffee_mud.Abilities.interfaces.*; import com.planet_ink.coffee_mud.Areas.interfaces.*; import com.planet_ink.coffee_mud.Behaviors.interfaces.*; import com.planet_ink.coffee_mud.CharClasses.interfaces.*; import com.planet_ink.coffee_mud.Commands.interfaces.*; import com.planet_ink.coffee_mud.Common.interfaces.*; import com.planet_ink.coffee_mud.Exits.interfaces.*; import com.planet_ink.coffee_mud.Items.interfaces.*; import com.planet_ink.coffee_mud.Libraries.interfaces.*; import com.planet_ink.coffee_mud.Locales.interfaces.*; import com.planet_ink.coffee_mud.MOBS.interfaces.*; import com.planet_ink.coffee_mud.Races.interfaces.*; import java.util.Date; import java.util.Enumeration; import java.util.Hashtable; import java.util.Map; /** * The Server class uses exactly one thread ServerThread object * during the course of program execution. This thread loops * until the Server class tells it to shut down. The loop is * executed in the thread's run() method. * @author George Reese (borg@imaginary.com), Bo Zimmerman * @version 1.1 * @see com.planet_ink.coffee_mud.core.intermud.i3.server.I3Server * * Modified in 2013 to cut down on thread use */ @SuppressWarnings({"unchecked","rawtypes"}) public class ServerThread implements Tickable { private java.util.Date boot_time=null; private int count = 1; private final String mud_name; private final int port; private boolean running; private final ImudServices intermuds; private ListenThread listen_thread=null; private volatile int tickStatus=Tickable.STATUS_NOT; private Map<String,ServerObject> objects; private Map<String,ServerUser> interactives; protected ServerThread(String mname, int mport, ImudServices imud) { mud_name = mname; port = mport; intermuds=imud; } @Override public String ID() { return "I3ServerThread"; } @Override public CMObject newInstance() { return this; } @Override public CMObject copyOf() { return this; } @Override public void initializeClass() { } @Override public int compareTo(CMObject o) { return (o==this)?0:1; } @Override public String name() { return "I3UserThread"+Thread.currentThread().getThreadGroup().getName().charAt(0); } @Override public int getTickStatus() { return tickStatus; } protected synchronized ServerObject copyObject(String str) throws ObjectLoadException { ServerObject ob; try { ob = (ServerObject)Class.forName(str).newInstance(); count++; str = str + "#" + count; ob.setObjectId(str); objects.put(str, ob); } catch( final Exception e ) { throw new ObjectLoadException("Failed to load object: " + e.getMessage()); } return ob; } protected synchronized ServerObject findObject(String str) throws ObjectLoadException { ServerObject ob; if( objects.containsKey(str) ) { ob = objects.get(str); if( ob.getDestructed() ) { ob = null; } } else { ob = null; } if( ob == null ) { try { ob = (ServerObject)Class.forName(str).newInstance(); ob.setObjectId(str); objects.put(str, ob); } catch( final Exception e ) { throw new ObjectLoadException("Failed to load object: " + e.getMessage()); } } return ob; } protected synchronized void removeObject(ServerObject ob) { final String id = ob.getObjectId(); if( !objects.containsKey(id) ) { return; } objects.remove(id); if( interactives.containsKey(id) ) { interactives.remove(id); } } /** * While the mud is running, this method repeats the following * steps over and over: * * Check for pending user input and trigger user commands * Check for pending object events and execute them * Check for incoming user connections and create an * interactive object for each. * */ public void start() { if( boot_time != null ) { Log.errOut("I3Server","Illegal attempt to invoke run()."); return; } try { listen_thread = new ListenThread(port); } catch( final java.io.IOException e ) { Log.errOut("I3Server",e); return; } try { Intermud.setup(intermuds, (PersistentPeer)Class.forName("com.planet_ink.coffee_mud.core.intermud.i3.IMudPeer").newInstance()); } catch( final Exception e ) { Log.errOut("I3Server",e); return; } boot_time = new java.util.Date(); Log.sysOut("I3Server", "InterMud3 Server started on port "+port); synchronized( this ) { objects = new Hashtable(1000, 100); interactives = new Hashtable(50, 20); } running = true; CMLib.threads().deleteTick(this, Tickable.TICKID_SUPPORT); CMLib.threads().startTickDown(this, Tickable.TICKID_SUPPORT, 250, 1); } @Override public boolean tick(Tickable ticking, int tickID) { if(CMSecurity.isDisabled(DisFlag.I3)) return running; tickStatus=Tickable.STATUS_ALIVE; ServerObject[] things; ServerUser[] users; synchronized( this ) { things = getObjects(); users = getInteractives(); } {// Process all input int i; for(i=0; i<users.length; i++) { final ServerUser interactive = users[i]; if( interactive.getDestructed() ) { continue; } try { interactive.processInput(); } catch( final Exception e ) { Log.errOut("IMServerThread",e); } } } {// Check for pending object events int i; for(i=0; i<things.length; i++) { final ServerObject thing = things[i]; if( !thing.getDestructed() ) { try { thing.processEvent(); } catch( final Exception e ) { Log.errOut("IMServerThread",e); } } } } {// Get new connections int i; for(i=0; i<5; i++) { java.net.Socket s; ServerUser new_user; if(listen_thread!=null) s = listen_thread.nextSocket(); else s=null; if( s == null ) { break; } try { new_user = (ServerUser)copyObject("com.planet_ink.coffee_mud.core.intermud.i3.IMudUser"); } catch( final ObjectLoadException e ) { continue; } try { new_user.setSocket(s); synchronized( this ) { interactives.put(new_user.getObjectId(), new_user); new_user.connect(); } } catch( final java.io.IOException e ) { new_user.destruct(); } } } tickStatus=Tickable.STATUS_NOT; return running; } protected Date getBootTime() { return boot_time; } protected synchronized ServerUser[] getInteractives() { final ServerUser[] tmp = new ServerUser[interactives.size()]; int i = 0; for(final ServerUser U : interactives.values()) { tmp[i++] = U; } return tmp; } protected String getMudName() { return mud_name; } protected int getPort() { return port; } public void shutdown() { running=false; Intermud.shutdown(); if(listen_thread!=null) { listen_thread.close(); CMLib.killThread(listen_thread,500,1); listen_thread=null; } boot_time = null; CMLib.threads().deleteTick(this, Tickable.TICKID_SUPPORT); } protected synchronized ServerObject[] getObjects() { final ServerObject[] tmp = new ServerObject[objects.size()]; int i = 0; for(final ServerObject O : objects.values()) { tmp[i++] = O; } return tmp; } }