protected String processGmcpStr(final Session session, final String jsonData, final Map<String,Double> supportables)
	{
		final MiniJSON jsonParser=new MiniJSON();
		try
		{
			final MOB mob=session.mob();
			final String allDoc=jsonData.trim();
			int pkgSepIndex=allDoc.indexOf(' ');
			String pkg;
			MiniJSON.JSONObject json;
			if(pkgSepIndex>0)
			{
				pkg=allDoc.substring(0,pkgSepIndex);
				final String jsonDoc=allDoc.substring(pkgSepIndex).trim();
				if(jsonDoc.length()==0)
					json=jsonParser.parseObject("{\"root\":{}}");
				else
				if(Character.isLetter(jsonDoc.charAt(0)))
					json=jsonParser.parseObject("{\"root\":\""+jsonDoc+"\"}");
				else
					json=jsonParser.parseObject("{\"root\":"+jsonDoc+"}");
			}
			else
			{
				pkg=allDoc;
				json=null;
			}
			GMCPCommand cmd;
			try
			{
				cmd=GMCPCommand.valueOf(pkg.toLowerCase().replace('.','_'));
				switch(cmd)
				{
				case maplevel:
					// what's this do?
					break;
				case request:
				{
					StringBuilder str=new StringBuilder(allDoc);
					str.setCharAt(pkgSepIndex, '_');
					return processGmcpStr(session,str.toString(),supportables);
				}
				case core_hello:
				{
					if(json!=null)
						json=json.getCheckedJSONObject("root");
					if(json != null)
					{
						final String client=json.getCheckedString("client");
						final Object o = json.get("version");
						double ver=0.0;
						if(o!=null)
						{
							String oStr=o.toString().trim();
							if(CMath.isNumber(oStr))
								ver=CMath.s_double(oStr);
						}
						if(client != null)
							supportables.put(client, Double.valueOf(ver));
					}
					break;
				}
				case core_supports_add:
				case core_supports_set:
				{
					Object[] list = null;
					if(json!=null)
						list=json.getCheckedArray("root");
					if(list != null)
					{
						for(final Object o : list)
						{
							String s=o.toString().toLowerCase().trim();
							double ver=1.0;
							final int x=s.indexOf(' ');
							if(x>0)
							{
								if(CMath.isNumber(s.substring(x+1).trim()))
									ver=CMath.s_double(s.substring(x+1).trim());
								s=s.substring(0,x).trim();
							}
							supportables.put(s, Double.valueOf(ver));
						}
					}
					break;
				}
				case core_supports_remove:
				{
					Object[] list = null;
					if(json!=null)
						list=json.getCheckedArray("root");
					if(list != null)
					{
						for(final Object o : list)
						{
							final String s=o.toString().trim();
							supportables.remove(s.toLowerCase());
						}
					}
					break;
				}
				case core_keepalive:
				{
					session.setIdleTimers();
					break;
				}
				case core_ping:
				{
					return pkg;
				}
				case core_goodbye:
				{
					session.stopSession(false,false,false);
					break;
				}
				case ire_composer_setbuffer:
				{
					String buf = null;
					if(json!=null)
						buf=json.getCheckedString("root");
					if(buf != null)
					{
						if(buf.indexOf('\n')>0)
						{
							buf=CMStrings.replaceAll(buf, "\n", "\\n");
							buf=CMStrings.replaceAll(buf, "\r", "");
						}
						else
						if(buf.indexOf('\r')>0)
						{
							buf=CMStrings.replaceAll(buf, "\r", "\\n");
							buf=CMStrings.replaceAll(buf, "\n", "");
						}
						session.setFakeInput(buf);
					}
					break;
				}
				case char_vitals:
				{
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("char.vitals {");
						doc.append("\"hp\":").append(mob.curState().getHitPoints()).append(",");
						doc.append("\"mana\":").append(mob.curState().getMana()).append(",");
						doc.append("\"moves\":").append(mob.curState().getMovement());
						doc.append("}");
						return doc.toString();
					}
					break;
				}
				case char_items_inv:
				{
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("char.items.list {");
						doc.append("\"location\":\"inv\",");
						doc.append("\"items\":[");
						for(int i=0;i<mob.numItems();i++)
						{
							final Item I=mob.getItem(i);
							if(I!=null)
							{
								doc.append("{");
								doc.append("\"id\":").append(I.hashCode()).append(",");
								doc.append("\"name\":\"").append(MiniJSON.toJSONString(I.Name())).append("\",");
								final String attribs = makeGMCPAttribs(I);
								if(attribs.length()>0)
									doc.append("\"attrib\":\"").append(attribs.toString()).append("\",");
								doc.append("}");
							}
						}
						doc.append("]");
						doc.append("}");
						return doc.toString();
					}
					break;
				}
				case char_items_contents:
				{
					if(mob != null)
					{
						if(json != null)
						{
							Long hashCode = json.getCheckedLong("root");
							final StringBuilder doc=new StringBuilder("char.items.list {");
							doc.append("\"location\":\""+hashCode+"\",");
							doc.append("\"items\":[");
							for(int i=0;i<mob.numItems();i++)
							{
								final Item I=mob.getItem(i);
								if(I!=null)
								{
									doc.append("{");
									doc.append("\"id\":").append(I.hashCode()).append(",");
									doc.append("\"name\":\"").append(MiniJSON.toJSONString(I.Name())).append("\",");
									final String attribs = makeGMCPAttribs(I);
									if(attribs.length()>0)
										doc.append("\"attrib\":\"").append(attribs.toString()).append("\",");
									doc.append("}");
								}
							}
							doc.append("]");
							doc.append("}");
							return doc.toString();
						}
					}
					break;
				}
				case char_statusvars:
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("char.statusvars {");
						doc.append("\"level\":\"").append(mob.phyStats().level()).append("\",");
						doc.append("\"race\":\"").append(MiniJSON.toJSONString(mob.charStats().raceName())).append("\"");
						final List<String> clans=new LinkedList<String>();
						for(final Pair<Clan,Integer> p : mob.clans())
							clans.add(MiniJSON.toJSONString(p.first.name()));
						if(clans.size()==1)
							doc.append(",\"guild\":\"").append(clans.get(0)).append("\"");
						else
						if(clans.size()>1)
						{
							doc.append(",\"guild\":[");
							for(int i=0;i<clans.size();i++)
							{
								if(i>0)
									doc.append(",");
								doc.append("\"").append(clans.get(i)).append("\"");
							}
							doc.append("]");
						}
						doc.append("}");
						return doc.toString();
					}
					break;
				case request_char:
				case char_base:
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("char.base {");
						doc.append("\"name\":\"").append(mob.name()).append("\"").append(",");
						doc.append("\"class\":\"").append(mob.charStats().getCurrentClass().baseClass()).append("\"").append(",");
						doc.append("\"subclass\":\"").append(MiniJSON.toJSONString(mob.charStats().displayClassName())).append("\"").append(",");
						doc.append("\"race\":\"").append(MiniJSON.toJSONString(mob.charStats().raceName())).append("\"").append(",");
						doc.append("\"perlevel\":").append(mob.getExpNextLevel());
						final String title = (mob.playerStats()!=null)?mob.playerStats().getActiveTitle():null;
						if(title!=null)
							doc.append(",\"pretitle\":\"").append(MiniJSON.toJSONString(title)).append("\"");
						final List<String> clans=new LinkedList<String>();
						for(final Pair<Clan,Integer> p : mob.clans())
							clans.add(MiniJSON.toJSONString(p.first.name()));
						if(clans.size()==1)
							doc.append(",\"clan\":\"").append(clans.get(0)).append("\"");
						else
						if(clans.size()>1)
						{
							doc.append(",\"clan\":[");
							for(int i=0;i<clans.size();i++)
							{
								if(i>0)
									doc.append(",");
								doc.append("\"").append(clans.get(i)).append("\"");
							}
							doc.append("]");
						}
						doc.append("}");
						return doc.toString();
					}
					break;
				case char_maxstats:
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("char.maxstats {");
						doc.append("\"maxhp\":").append(mob.maxState().getHitPoints()).append(",");
						doc.append("\"maxmana\":").append(mob.maxState().getMana()).append(",");
						doc.append("\"maxmoves\":").append(mob.maxState().getMovement());
						doc.append("}");
						return doc.toString();
					}
					break;
				case char_status:
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("char.status {");
						doc.append("\"level\":").append(mob.phyStats().level()).append(",");
						doc.append("\"tnl\":").append(mob.getExpNeededLevel()).append(",");
						doc.append("\"hunger\":").append(mob.curState().getHunger()).append(",");
						doc.append("\"thirst\":").append(mob.curState().getThirst()).append(",");
						doc.append("\"fatigue\":").append(mob.curState().getFatigue()).append(",");
						final int align=mob.fetchFaction(CMLib.factions().AlignID());
						if(align!=Integer.MAX_VALUE)
							doc.append("\"align\":").append(align).append(",");
						int state=3;
						if(session.isAfk())
							state=4;
						else
						if(CMLib.flags().isSleeping(mob))
							state=9;
						else
						if(CMLib.flags().isSitting(mob))
							state=11;
						else
						if(mob.getVictim()!=null)
							state=8;
						else
						if(mob.isAttributeSet(MOB.Attrib.SYSOPMSGS))
							state=6;
						else
						if(mob.isAttributeSet(MOB.Attrib.AUTORUN))
							state=12;
						doc.append("\"state\":").append(state).append(",");
						doc.append("\"pos\":\"").append(
								CMLib.flags().isSleeping(mob)?"Sleeping":
								CMLib.flags().isSitting(mob)?"Sitting":
								"Standing"
								).append("\"");
						final MOB vicM=mob.getVictim();
						if(vicM!=null)
						{
							doc.append(",");
							doc.append("\"enemy\":\"").append(vicM.name()).append("\",");
							doc.append("\"enemypct\":").append(Math.round((double)vicM.maxState().getHitPoints()/(double)vicM.curState().getHitPoints()*100.0));
						}
						doc.append("}");
						return doc.toString();
					}
					else
					{
						final StringBuilder doc=new StringBuilder("char.status {");
						doc.append("\"state\":").append(1);
						doc.append("}");
						return doc.toString();
					}
				case char_worth:
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("char.worth {");
						doc.append("\"gold\":").append(CMLib.beanCounter().getTotalAbsoluteNativeValue(mob)).append(",");
						doc.append("\"qp\":").append(mob.getQuestPoint()).append(",");
						//doc.append("\"tp\":").append(mob.getTrains()).append(",");
						doc.append("\"trains\":").append(mob.getTrains()).append(",");
						doc.append("\"pracs\":").append(mob.getPractices());
						doc.append("}");
						return doc.toString();
					}
					break;
				case request_area:
				case request_sectors:
				case request_room:
				case room_info:
					if(mob!=null)
					{
						final Room room=mob.location();
						if(room != null)
						{
							final StringBuilder doc=new StringBuilder("room.info {");
							final String roomID=CMLib.map().getExtendedRoomID(room);
							final String domType;
							if((room.domainType()&Room.INDOORS)==0)
								domType=Room.DOMAIN_OUTDOOR_DESCS[room.domainType()];
							else
								domType=Room.DOMAIN_INDOORS_DESCS[CMath.unsetb(room.domainType(),Room.INDOORS)];
							doc.append("\"num\":").append(roomID.hashCode()).append(",")
								.append("\"id\":\"").append(roomID).append("\",")
								.append("\"name\":\"").append(MiniJSON.toJSONString(room.displayText(mob))).append("\",")
								.append("\"zone\":\"").append(MiniJSON.toJSONString(room.getArea().name())).append("\",")
								.append("\"terrain\":\"").append(domType.toLowerCase()).append("\",")
								.append("\"details\":\"").append("\",")
								.append("\"exits\":{");
							boolean comma=false;
							for(int d=0;d<Directions.NUM_DIRECTIONS();d++)
							{
								final Room R2=room.getRoomInDir(d);
								if((R2!=null)&&(room.getExitInDir(d)!=null))
								{
									final String room2ID=CMLib.map().getExtendedRoomID(R2);
									if(room2ID.length()>0)
									{
										if(comma)
											doc.append(","); comma=true;
										doc.append("\""+CMLib.directions().getDirectionChar(d)+"\":").append(room2ID.hashCode());
									}
								}
							}
							doc.append("},\"coord\":{\"id\":0,\"x\":-1,\"y\":-1,\"cont\":0}");
							doc.append("}");
							return doc.toString();
						}
					}
					break;
				case request_quest:
					// comm.quest responds whenever quest stuff happens..
					// comm.quest {"action": "start", "targ": "a swamp ape", "room": "Swamp Ape Enclosure", "area": "Aardwolf Zoological Park", "timer": 52 }
					// for request quest, a simple response:
					// comm.quest {"action": "status", "targ": "A spirit of strong essence", "room": "The Path of the Dead", "area": "The Deadlights", "timer": 60 }
					return "comm.quest {\"action\": \"status\", \"status\": \"ready\" }";
				case rawcolor:
					// ardwolf stuff -- no idea
					break;
				case request_group:
				case group:
					if(mob!=null)
					{
						final StringBuilder doc=new StringBuilder("group {");
						final Set<MOB> group=mob.getGroupMembers(new HashSet<MOB>());
						final MOB leaderM=(mob.amFollowing()==null)?mob:mob.amUltimatelyFollowing();
						doc.append("\"groupname\":\"").append(leaderM.name(mob)).append("s group").append("\",")
							.append("\"leader\":\"").append(leaderM.name(mob)).append("\",")
							.append("\"status\":\"").append("Private").append("\",")
							.append("\"count\":").append(group.size()).append(",")
							.append("\"members\":[");
						boolean comma=false;
						for(final MOB M : group)
						{
							if(comma)
								doc.append(",");
							comma=true;
							doc.append("{\"name\":\"").append(M.name(mob)).append("\",")
								.append("{\"info\":{")
								.append("\"hp\":").append(M.curState().getHitPoints()).append(",")
								.append("\"mhp\":").append(M.maxState().getHitPoints()).append(",")
								.append("\"mn\":").append(M.curState().getMana()).append(",")
								.append("\"mmn\":").append(M.maxState().getMana()).append(",")
								.append("\"mv\":").append(M.curState().getMovement()).append(",")
								.append("\"mmv\":").append(M.maxState().getMovement()).append(",")
								.append("\"lvl\":").append(M.phyStats().level()).append(",");
							final int align=mob.fetchFaction(CMLib.factions().AlignID());
							if(align!=Integer.MAX_VALUE)
								doc.append("\"align\":").append(align).append(",");
							doc.append("\"tnl\":").append(M.getExpNeededLevel());
							doc.append("}");
						}
						doc.append("]");
						doc.append("}");
						return doc.toString();
					}
					break;
				case comm_channel:
					break;
				case comm_channel_players:
					if(mob != null)
					{
						final StringBuilder doc=new StringBuilder("comm.channel.players [");
						for(final Session sess : CMLib.sessions().allIterable())
						{
							if(sess != null)
							{
								MOB M=sess.mob();
								if((M!=null)&&(M.soulMate()!=null))
									M=M.soulMate();
								if((M != null)
								&&(M!=mob)
								&&(((!CMLib.flags().isCloaked(M))
									||((CMSecurity.isAllowedAnywhere(mob,CMSecurity.SecFlag.CLOAK)||CMSecurity.isAllowedAnywhere(mob,CMSecurity.SecFlag.WIZINV))&&(mob.phyStats().level()>=M.phyStats().level()))))
								&&(M.phyStats().level()>0))
								{
									doc.append("{");
									doc.append("\"name\":\""+MiniJSON.toJSONString(M.name(mob))).append(",");
									doc.append("\"channels\":[");
									for(int i=0;i<CMLib.channels().getNumChannels();i++)
									{
										ChannelsLibrary.CMChannel channel=CMLib.channels().getChannel(i);
										if(CMLib.channels().mayReadThisChannel(mob, true, M, i, false))
											doc.append("\"").append(MiniJSON.toJSONString(channel.name().toLowerCase())).append("\",");
									}
									if(doc.charAt(doc.length()-1)==',')
										doc.deleteCharAt(doc.length()-1);
									doc.append("]}");
								}
							}
						}
						doc.append("]");
					}
					break;
				case char_skills_get:
					if(json!=null)
						json=json.getCheckedJSONObject("root");
					if((json != null)&&(mob!=null))
					{
						String group=null;
						String name=null;
						if(json.containsKey("group"))
							group=json.getCheckedString("group").toLowerCase().trim();
						if(json.containsKey("name"))
							name=json.getCheckedString("name").toLowerCase().trim();
						if((group != null)
						&&(group.length()>0)
						&&((name==null)||(name.length()==0)))
						{
							final Map<Integer,List<Ability>> allMyGroups=getSkillGroups(mob);
							final StringBuilder doc=new StringBuilder("char.skills.list {");
							for(Integer grp : allMyGroups.keySet())
							{
								final String groupName=this.getAbilityGroupName(grp.intValue());
								if(groupName.equals(group))
								{
									doc.append("\"group\":\""+MiniJSON.toJSONString(groupName)+"\",");
									doc.append("\"list\":[");
									for(final Ability A : allMyGroups.get(grp))
										doc.append("\"").append(MiniJSON.toJSONString(A.name().toLowerCase())).append("\",");
									doc.setCharAt(doc.length()-1,']');
									break;
								}
							}
							doc.append("}");
							return doc.toString();
						}
						if((group != null)&&(group.length()>0)&&(name!=null)&&(name.length()!=0))
						{
							final StringBuilder doc=new StringBuilder("char.skills.info {");
							for(int a=0;a<mob.numAllAbilities();a++)
							{
								final Ability A=mob.fetchAbility(a);
								if((A!=null)&&(A.name().toLowerCase().equals(name)))
								{
									doc.append("\"group\":\""+MiniJSON.toJSONString(getAbilityGroupName(A.abilityCode()))+"\",");
									doc.append("\"skill\":\""+MiniJSON.toJSONString(name)+"\",");
									doc.append("\"info\":\""+MiniJSON.toJSONString(CMLib.help().getHelpText(A.Name().toUpperCase(), mob, false).toString())+"\",");
								}
							}
							doc.append("}");
							return doc.toString();
						}
						else
						{
							final StringBuilder doc=new StringBuilder("char.skills.groups [");
							final Map<Integer,List<Ability>> allMyGroups=getSkillGroups(mob);
							if(allMyGroups.size()>0)
							{
								for(Integer grp : allMyGroups.keySet())
								{
									final String groupName=this.getAbilityGroupName(grp.intValue());
									doc.append("\"").append(MiniJSON.toJSONString(groupName)).append("\",");
								}
								doc.setCharAt(doc.length()-1,']');
							}
							else
								doc.append("]");
							return doc.toString();
						}
					}
					break;
				default:
					break;
				}
				return null;
			}
			catch(final Exception e)
			{
				Log.errOut("Error parsing GMCP Package: "+pkg+": "+e.getMessage());
				if(CMSecurity.isDebugging(DbgFlag.GMCP))
					Log.errOut(e);
				return null;
			}
		}