タスク自体について少し。
ブラウザRPGエンジンを書いています。 すべてのゲームロジック(場所間の遷移、購入など)はTomcat 6によって処理されます。ページはJSONオブジェクトを要求し、サーバーは要求を処理し、結果を返します。 最初は、アクションのグループごとに個別のサーブレットがありましたが、多くのコードと重複がありました。 一般的に、私は好きではなかった。
サーバーへのすべての呼び出しは、2つのタイプに分けられます。
1.オブジェクトの状態(プレイヤーの名前、レベル、その場所にいるプレイヤーのリスト)を取得する
2.オブジェクトの状態の変化(アイテムの購入、アイテムの組み立て、メッセージの送信)
2番目のタイプの処理も簡略化しましたが、次回はそれについてです。
アイデアは、結果を取得するための簡単なアルゴリズムを構築するために、すべてのオブジェクトを一般化することです。
これを行うために、インターフェイスを使用し、オブジェクトで実装しました。
package engine.core.obj;
import org.json.JSONObject;
/**
*
* @author chernser
* Interface used to work with core objects
*
*/
public interface ICoreObject {
/**
* Should implement encoding into
* JSON notation
* @param context TODO
*
* @return returns object in JSON notation
* @return null if error
*/
public JSONObject toJSON(Context context);
/**
*
* Should implement getting object's class
* name
*
* @return returns object class name
* @return null if error
*/
public String getClassName();
/**
* Should find child object by name
* and return it
* @param context TODO
*
* @return returns child object
* @return null if error
*/
public ICoreObject getChild(String name, Context context);
/**
* Should find child object
* by name and index then return it
* @param context TODO
*
* @return returns child object
* @return null if error
*/
public ICoreObject getChild(String name, Integer index, Context context);
/**
* Should return parameter's value
*
* @param name - parameter name
* @param context - current context
* @return returns value of parameter
* @return null if no such parameter
*
*/
public Object getParameter(String name, Context context);
}
* This source code was highlighted with Source Code Highlighter .
主な方法は次のとおりです。
- toJSON()-オブジェクトの実際の形成
- getChild()-現在のオブジェクトの子孫を返します
- getParameter()-プレーヤーの名前など、オブジェクトのパラメーターの1つを返します
これらのメソッドの実装のいくつかの例:
@Override
public ICoreObject getChild(String name, Context context) {
if (name.equals(AvatarBaseParams.CORE_OBJ_NAME)) {
return itsBaseParams;
} else if (name.equals(Location.CORE_OBJ_NAME)) {
return itsLocation;
} else if (name.equals(AvatarInventory.CORE_OBJ_NAME))
{
return itsInventory;
} else if (name.equals(AvatarSkills.CORE_OBJ_NAME))
{
return itsSkills;
} else if (name.equals(AvatarModifications.CORE_OBJ_NAME))
{
return itsModifications;
}
return null ;
}
* This source code was highlighted with Source Code Highlighter .
@Override
public ICoreObject getChild(String name, Context context) {
if (name.equals(AvatarBaseParams.CORE_OBJ_NAME)) {
return itsBaseParams;
} else if (name.equals(Location.CORE_OBJ_NAME)) {
return itsLocation;
} else if (name.equals(AvatarInventory.CORE_OBJ_NAME))
{
return itsInventory;
} else if (name.equals(AvatarSkills.CORE_OBJ_NAME))
{
return itsSkills;
} else if (name.equals(AvatarModifications.CORE_OBJ_NAME))
{
return itsModifications;
}
return null ;
}
* This source code was highlighted with Source Code Highlighter .
通常、このメソッドは単純なディスパッチャです。 この方法のおかげで、任意の階層を構築できます。
@Override
public JSONObject toJSON(Context context) {
JSONObject result = new JSONObject();
try {
result.put( "name" , itsName);
result.put( "bparams" , itsBaseParams.toJSON(context));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return result;
}
* This source code was highlighted with Source Code Highlighter .
@Override
public JSONObject toJSON(Context context) {
JSONObject result = new JSONObject();
try {
result.put( "name" , itsName);
result.put( "bparams" , itsBaseParams.toJSON(context));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return result;
}
* This source code was highlighted with Source Code Highlighter .
@Override
public JSONObject toJSON(Context context) {
JSONObject result = new JSONObject();
try {
result.put( "name" , itsName);
result.put( "bparams" , itsBaseParams.toJSON(context));
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return result;
}
* This source code was highlighted with Source Code Highlighter .
ここで、必要なフィールドを持つJSONObjectを作成します。
したがって、一般的なルールに従うオブジェクトがあります。 チェーン全体をすばやくトラバースし、結果を返すためのアルゴリズムが必要です。 ここで、おそらく、最も難しい瞬間:-)
クエリ文字列は次のようになります。
user.avatar:name-アバターオブジェクトの名前を返します。 パラメーターへのアクセスにはコロンが使用されます。
user.avatar.location-ロケーションオブジェクトのJSON表現を返します。
user.avatar.inventory.slot [4]-プレーヤーのインベントリの4番目のスロットのJSON表現を返します。
リクエストは別のサーブレットに入り、これがリクエストの処理方法です。
1.リクエストと戻り値の初期チェック
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/* Set encoding for request to properly parse parameters */
request.setCharacterEncoding( "UTF-8" );
// get user from session
User user = (User) request.getSession().getAttribute(
CommonConstants.USER);
// save query and prelimenary result
String query = request.getParameter( "query" );
String result = "{error: -1}" ;
// check if we have user
if (user != null )
{
Context context = user.itsContext;
// Check parameters
if (query != null )
{
// process query
result = ParseAndExecuteQuery(query, request.getSession(), context);
}
}
/* tune response to avoid problem with Russian */
response.setContentType( "text/javascript; charset=UTF-8" );
response.setHeader( "Cache-Control" , "no-cache" ); //HTTP 1.1
response.setHeader( "Pragma" , "no-cache" ); //HTTP 1.0
response.setDateHeader( "Expires" , 0); //prevents caching at the proxy server
response.getWriter().write(result);
}
* This source code was highlighted with Source Code Highlighter .
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/* Set encoding for request to properly parse parameters */
request.setCharacterEncoding( "UTF-8" );
// get user from session
User user = (User) request.getSession().getAttribute(
CommonConstants.USER);
// save query and prelimenary result
String query = request.getParameter( "query" );
String result = "{error: -1}" ;
// check if we have user
if (user != null )
{
Context context = user.itsContext;
// Check parameters
if (query != null )
{
// process query
result = ParseAndExecuteQuery(query, request.getSession(), context);
}
}
/* tune response to avoid problem with Russian */
response.setContentType( "text/javascript; charset=UTF-8" );
response.setHeader( "Cache-Control" , "no-cache" ); //HTTP 1.1
response.setHeader( "Pragma" , "no-cache" ); //HTTP 1.0
response.setDateHeader( "Expires" , 0); //prevents caching at the proxy server
response.getWriter().write(result);
}
* This source code was highlighted with Source Code Highlighter .
2.リクエストの準備と初期オブジェクトの決定(ユーザーと世界の2つがあります)
private String ParseAndExecuteQuery(String query, HttpSession session, Context context)
{
ICoreObject rootObject = null ;
// if query starts from "user." Also check syntax
if (query.matches( "user\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (User) session.getAttribute(CommonConstants.USER);
}
// if query starts from "world." Also check syntax
else if (query.matches( "world\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (World)session.getServletContext().
getAttribute(CommonConstants.WORLD);
}
// Check if we have root object
if (rootObject != null )
{
// Start processing
ICoreObject result = getObject(rootObject, query, context);
if (result != null )
{
// Here we got result
JSONObject jsonObj = result.toJSON(context);
if (jsonObj != null )
{
// return it
return jsonObj.toString();
}
}
}
return "" ;
}
* This source code was highlighted with Source Code Highlighter .
private String ParseAndExecuteQuery(String query, HttpSession session, Context context)
{
ICoreObject rootObject = null ;
// if query starts from "user." Also check syntax
if (query.matches( "user\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (User) session.getAttribute(CommonConstants.USER);
}
// if query starts from "world." Also check syntax
else if (query.matches( "world\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (World)session.getServletContext().
getAttribute(CommonConstants.WORLD);
}
// Check if we have root object
if (rootObject != null )
{
// Start processing
ICoreObject result = getObject(rootObject, query, context);
if (result != null )
{
// Here we got result
JSONObject jsonObj = result.toJSON(context);
if (jsonObj != null )
{
// return it
return jsonObj.toString();
}
}
}
return "" ;
}
* This source code was highlighted with Source Code Highlighter .
private String ParseAndExecuteQuery(String query, HttpSession session, Context context)
{
ICoreObject rootObject = null ;
// if query starts from "user." Also check syntax
if (query.matches( "user\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (User) session.getAttribute(CommonConstants.USER);
}
// if query starts from "world." Also check syntax
else if (query.matches( "world\\.([a-zA-Z_]+(\\[[0-9]+\\])*(:[a-zA-Z_]+)*\\.{0,1})+" ))
{
rootObject = (World)session.getServletContext().
getAttribute(CommonConstants.WORLD);
}
// Check if we have root object
if (rootObject != null )
{
// Start processing
ICoreObject result = getObject(rootObject, query, context);
if (result != null )
{
// Here we got result
JSONObject jsonObj = result.toJSON(context);
if (jsonObj != null )
{
// return it
return jsonObj.toString();
}
}
}
return "" ;
}
* This source code was highlighted with Source Code Highlighter .
3.目的のオブジェクトを見つける
private ICoreObject getObject(ICoreObject rootObject, String query, Context context)
{
ICoreObject curObject = rootObject; // current object
query = query.substring(query.indexOf( "." ) + 1) + "." ; // query
Integer dotIndex = query.indexOf( "." );
String tmpObjName = "" ; // object name
do {
String objName = query.substring(0, dotIndex); // get first object in chain
// if by index
if (objName.matches( "[a-zA-Z_]+\\[[0-9]+\\](:[a-zA-Z0-9_]+)*" ))
{
Integer index = new Integer(objName.substring(objName.indexOf( "[" ) + 1,
objName.indexOf( "]" )));
tmpObjName = objName.substring(0, objName.indexOf( "[" ));
curObject = curObject.getChild(tmpObjName, index, context);
}
else // if not by index
{
Integer semiIndex = 0;
if ((semiIndex = objName.indexOf( ":" )) > -1)
curObject = curObject.getChild(objName.substring(0, semiIndex), context);
else
curObject = curObject.getChild(objName, context);
}
// If current object in chain contains ':' - then proccess it as parameter
if (objName.matches( "[a-zA-Z_]+(\\[[0-9]+\\])*:[a-zA-Z0-9_]+" ))
{
// get parameter name
String param = objName.substring(objName.indexOf( ":" ) + 1);
return new CoreObjectParameter(param, curObject.getParameter(param, context));
}
// cut off current object name and step forward
query = query.substring(dotIndex + 1);
dotIndex = query.indexOf( "." );
} while ((dotIndex > -1) && (curObject != null ));
return curObject;
}
* This source code was highlighted with Source Code Highlighter .
private ICoreObject getObject(ICoreObject rootObject, String query, Context context)
{
ICoreObject curObject = rootObject; // current object
query = query.substring(query.indexOf( "." ) + 1) + "." ; // query
Integer dotIndex = query.indexOf( "." );
String tmpObjName = "" ; // object name
do {
String objName = query.substring(0, dotIndex); // get first object in chain
// if by index
if (objName.matches( "[a-zA-Z_]+\\[[0-9]+\\](:[a-zA-Z0-9_]+)*" ))
{
Integer index = new Integer(objName.substring(objName.indexOf( "[" ) + 1,
objName.indexOf( "]" )));
tmpObjName = objName.substring(0, objName.indexOf( "[" ));
curObject = curObject.getChild(tmpObjName, index, context);
}
else // if not by index
{
Integer semiIndex = 0;
if ((semiIndex = objName.indexOf( ":" )) > -1)
curObject = curObject.getChild(objName.substring(0, semiIndex), context);
else
curObject = curObject.getChild(objName, context);
}
// If current object in chain contains ':' - then proccess it as parameter
if (objName.matches( "[a-zA-Z_]+(\\[[0-9]+\\])*:[a-zA-Z0-9_]+" ))
{
// get parameter name
String param = objName.substring(objName.indexOf( ":" ) + 1);
return new CoreObjectParameter(param, curObject.getParameter(param, context));
}
// cut off current object name and step forward
query = query.substring(dotIndex + 1);
dotIndex = query.indexOf( "." );
} while ((dotIndex > -1) && (curObject != null ));
return curObject;
}
* This source code was highlighted with Source Code Highlighter .
private ICoreObject getObject(ICoreObject rootObject, String query, Context context)
{
ICoreObject curObject = rootObject; // current object
query = query.substring(query.indexOf( "." ) + 1) + "." ; // query
Integer dotIndex = query.indexOf( "." );
String tmpObjName = "" ; // object name
do {
String objName = query.substring(0, dotIndex); // get first object in chain
// if by index
if (objName.matches( "[a-zA-Z_]+\\[[0-9]+\\](:[a-zA-Z0-9_]+)*" ))
{
Integer index = new Integer(objName.substring(objName.indexOf( "[" ) + 1,
objName.indexOf( "]" )));
tmpObjName = objName.substring(0, objName.indexOf( "[" ));
curObject = curObject.getChild(tmpObjName, index, context);
}
else // if not by index
{
Integer semiIndex = 0;
if ((semiIndex = objName.indexOf( ":" )) > -1)
curObject = curObject.getChild(objName.substring(0, semiIndex), context);
else
curObject = curObject.getChild(objName, context);
}
// If current object in chain contains ':' - then proccess it as parameter
if (objName.matches( "[a-zA-Z_]+(\\[[0-9]+\\])*:[a-zA-Z0-9_]+" ))
{
// get parameter name
String param = objName.substring(objName.indexOf( ":" ) + 1);
return new CoreObjectParameter(param, curObject.getParameter(param, context));
}
// cut off current object name and step forward
query = query.substring(dotIndex + 1);
dotIndex = query.indexOf( "." );
} while ((dotIndex > -1) && (curObject != null ));
return curObject;
}
* This source code was highlighted with Source Code Highlighter .
オブジェクトが見つかった場合、JSONスクリプトとしてページに送信され、見つからない場合は空のオブジェクトが返されます。
メカニズムはまだベータ版です:-)ただし、これまでのところ、バグは示されていません。