Pivot 1.4、Spring 和 Hibernate 是我的 RPG 游戏





5.00/5 (4投票s)
Pivot 1.4、Spring 和 Hibernate 是我的 RPG 游戏
目录
- 引言
- 库和角色
- 环境配置
- 如果您需要排查故障!
- 简单的应用程序
- 应用程序架构 (MVC)
- 制作“魔法物品”
- 映射!POJO 和 DAO “模型”
- 创建 Pivot WTKX “视图”
- 与魔法物品“控制器”的链接
- 摘要
- 历史
0. 引言

在开始本文之前,我想澄清一下,这不是“我的 RPG 游戏”,而是“我们的 RPG 游戏”,实际上这是“我们的 MVC 游戏”,通过使用 Apache 的一款名为 Pivot 的新型 富互联网应用程序 (RIA) 来实现。
Pivot!这是什么?“Apache Pivot 是一个使用 Java 构建富互联网应用程序的开源平台。它结合了现代 RIA 工具包的增强的生产力和可用性功能与 Java 平台的健壮性。 Pivot 应用程序 使用 Java 和 XML 的组合编写,可以作为小程序或独立的、可选离线的、桌面应用程序运行。”
桌面应用程序!真的吗?是的,这是我们通过使用模型-视图-控制器 (MVC) 模型-视图-控制器架构而无需使用任何其他 MVC 框架和/或库的简单演示应用程序。
1. 库和角色
就像经典的 RPG 游戏(仙境传说游戏)一样,我们有三个经典的“职业”、“任务”或“种族”。战士、牧师和法师,对于每个“职业”,我们都有库 (jar) 或框架来引用它在这个应用程序中。
让我们继续讨论我们的英雄
- Pivot 扮演战士的角色,因为它在前端,通过它的盾牌“模型和弹出消息”来保护应用程序免受用户攻击(鼠标点击、无效数据等)=),这很有趣?
- Spring 扮演牧师的角色,哦不!Spring 是一个法师,因为它使用神奇的配置来链接所有应用程序库和/或框架。
- Hibernate 扮演牧师的角色,因为它从数据库中检索数据来治愈和祝福 Pivot,以免在用户前端丢失。
现在,您对我们的英雄有所了解,但在开始之前,我们需要与称为 Maven 和 SpringIDE 的 NPC 交谈!
2. 环境配置
要与 Maven 和 SpringIDE NPC 对话,我们需要配置我们的环境。首先,如果您没有 MySQL 服务器,请尝试从此处下载并安装它,这很简单。
完成配置后,我们从 eAthena 私服 下载了一个 Ragnarok 数据库脚本
- mob_db.sql
- item_db.sql
现在输入
mysql -uroot
如果您没有为“root”用户设置任何密码,或者
mysql -uroot -p
如果您为 mysql “root”用户设置了密码。

最后,我们使用此脚本将数据导入数据库
CREATE DATABASE Ragnarok;
USE Ragnarok;
\. [your-path]/mob_db.sql
\. [your-path]/item_db.sql
在安装、配置 MySQL 并导入 Ragnarok 数据后,我们按照 这些步骤 安装 Maven (不要错过这一部分,这样您就不需要稍后回来)。
现在,是时候在 Eclipse 中配置 Maven 了。Eclipse!为什么?这是一个我们可以找到的出色的 IDE,因为许多插件可以方便开发(我不是 Eclipse 的粉丝,我使用它是为了作弊=)),好的,如果您没有 Eclipse JEE,请尝试从此处下载。
在我们尝试在此 Eclipse IDE 中安装此插件后
- 选择 Help > Install New Software。这将显示“Install”对话框。
- 将更新站点 URL(http://m2eclipse.sonatype.org/sites/m2e)粘贴到名为“Work with:”的字段中,然后按 Enter。按 Enter 后,Eclipse 应该会更新可用插件和组件的列表。
- 选择 m2eclipse 下列出的组件:“Maven Integration for Eclipse (Required)”。
- 点击 Next。Eclipse 将检查是否存在任何可能阻止成功安装的问题。
- 点击 Next 并同意 Eclipse Public License v1.0 的条款。
- 点击 Finish 开始安装过程。Eclipse 将下载并安装必要的组件。
- 安装过程完成后,Eclipse 会询问您是否要重新启动 IDE。Sonatype 强烈建议您在安装 m2eclipse 后重新启动 IDE。
我们还从 此处 安装 SpringIDE (步骤类似)。

非常好,现在我们创建我们的项目只是为了展示为什么我们在本文中使用了 Maven 插件——我们就此停止——在 Eclipse 中,我们创建一个新的 Maven 项目,我们称之为“ragnarok-spring-hibernate”,正如您在此处看到的

接下来,我们勾选“Create a simple project (skip archtype selection)”

Next,在 Group Id: “ro.springhibernate”,以及 Artifact:“ragnarok-spring-hibernate”

如果项目创建成功,我们就将其转换为 spring 项目,别担心,这很容易!非常容易!也许您还记得 SpringIDE,现在是时候使用它了。只需右键单击项目,我们在上下文菜单底部找到“Spring tools”,然后单击“Add Spring Project Nature”!

呼,最后我们将此代码粘贴到 POM.xml 文件中,Maven 会自动下载所有必需的库,而无需任何努力或基本知识。(只需在 POM 文件中 </project> 之前复制粘贴即可。)
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.6.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.4.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.3.0.GA</version>
</dependency>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-core</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-wtk</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.apache.pivot</groupId>
<artifactId>pivot-wtk-terra</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.4.2</version>
</dependency>
</dependencies>
“第一个任务完成!”
2.0 - 如果您需要排查故障!
在许多书籍或教程中,这一部分被忽略了,读者也因此感到沮丧,仅仅因为他们不想被称作“菜鸟”,就去当“菜鸟”。第一次不是世界末日=)。好吧,我们回来了。
如果您在重新启动 Eclipse 时被一个非常温和的错误“Maven integration requires...”所震惊。

这很容易解决,如果您使用 Windows,只需创建一个“.BAT”文件——而且您很懒——您不想每次打开 Eclipse 时都重复此步骤,可以尝试将此代码写入 BAT 文件并执行它
eclipse.exe -vm “path to your JDK or JRE/bin”
示例
eclipse -vm "C:\Program Files\Java\jdk1.6.0_13\bin"
如果您有任何问题,请排查 Maven 依赖项(下载的库)或找不到库的问题!此时您需要自己解决,尝试在 http://mirrors.ibiblio.org/pub/mirrors/maven2/ 或任何 Maven 存储库中搜索丢失的“jar”,下载 3 个文件
- your-missed-jar.JAR
- your-missed-jar.JAR.MD5
- your-missed-jar.JAR.SHA1
并手动安装它们。最后尝试重新启动 Eclipse 或保存 POM.xml 文件进行自动检查
mvn install:install-file -Dfile=your-missed-jar.JAR -DgroupId=org.some.group
-DartifactId=your-artifact -Dversion=jar-version -Dpackaging=jar
稍等!我在哪里可以找到 DgroupId
(GroupId
), DartifactId
(ArtifactId
), Dversion
(Version
)?
绝佳的问题,只需查看 Eclipse 控制台,您就会找到所有所需的信息!

3. 简单应用程序
我们的简单应用程序的代码名为“RoManager
”,用于管理,正如您所注意到的,它可以管理怪物和物品,用例图

好的!我们对应用程序的目标有了一个了解!但系统是如何工作的?
RoManager
主要使用 Pivot 来创建用户界面 UI(用于绑定 WTKX 文件的视图 Java 类),使用 Spring 进行配置并创建由控制器使用的 bean(控制器是一个监听器,我们将在后面详细解释)。
等等等等,"roempire.com" 的作用是什么?
哦!你真的想知道吗?呃,这是一个微小的技巧,用于从 RoEmpire 在线数据库检索怪物和物品图像。我们不需要下载 Ragnarok 客户端(+3GB)并解压它来获取精灵!?而且这些东西是文章中的一个奖励,您可以在许多领域中使用它!例如作为 Web 服务、RSS 等。
4. 应用程序架构 (MVC)
在本文中,我们拥有与 ASP.NET MVC 类似的架构。请看资源管理器图像,不是相似的功能,我是一个坏人吗?
我不知道,但我只是想组织应用程序代码,并找到了一种简单的架构,这不是标准的组织方式吗?让我们比较一下我们的 MVC 架构和微软的!——默认项目——
我们的 MVC 架构 | 微软 ASP.NET MVC 架构 |
![]() |
![]() |
我们的架构比微软的好!这是因为它是一个桌面 MVC,而不是 Web MVC。
我们有一个“models”文件夹。此文件夹仅包含所有映射类 POJO 和 DAO,不允许其他内容。
“controllers”源文件夹仅包含控制器(Listeners
类),按包分组,每个包名都是“View
”类的名称,以按 Java UI 类(视图)对控制器进行分组,例如
- 视图名称 (
RoManager
) - 在 controllers 源文件夹中,一个名为“
romanager
”的包包含View RoManager
的所有控制器。
而“views”源文件夹仅包含所有 Java UI 类!不包含 WTKX 文件。
但 WTKX 文件在哪里?
WTKX 文件不是源代码——它们无法编译——因此我将它们从“views”源文件夹移到了“resources”文件夹。我个人喜欢将源文件分开,它们可以像 Java 一样从文本文件(如 WTKX)编译。
最后,“java”源文件夹包含所有新开发的内容,例如“Linker
”类或其他。
“钥匙找到了!”
5. 制作“魔法物品”
“欢迎来到尼福尔海姆。”
如果您不理解、感到困惑或需要解决本文中的此部分问题,请不要担心,如果您对 Java 反射及其技术(如 控制反转)不感兴趣。您可以轻松跳过它,而不必查看 Linker
类的内容,只需使用它即可。
但是,如果您感兴趣,可以在阅读本文后,了解另一个高级概念以及我们可以用一个简单的 Java 反射库做什么。
“魔法物品”,一个 Linker
类有两个目标
- 将
Listeners
类(我们称之为控制器)与 Pivot Java UI 类(我们在本文中称为视图的绑定组件的类)分离。 - 提供一种简单的控制器更改方式,而无需重新编译源代码。
– Linker 的工作方式类似于 Spring –
从配置文件读取并将其 Controllers
添加到特定组件,您是否感到困惑?没问题,睁大眼睛看。
首先,请查看 Java 源代码 此处,不要玩弄别人的例子!
25 // Add a button press listener
26 pushButton.getButtonPressListeners().add(new ButtonPressListener() {
27 @Override
28 public void buttonPressed(Button button) {
29 Alert.alert(MessageType.INFO, "You clicked me!", window);
30 }
31 });
对我们而言,[1] Listener = Controller
和 [2] Listener = ButtonPressListener
。
从 [1] 和 [2] 中,我们可以注意到 Controller
包含在 Java UI 类中,这违背了我们架构的理论。因此,创建了 Linker
来通过配置文件将分离的“Listeners
”链接到绑定的组件。
实现分离的 Controllers
与 Views
。
第一个名为“bind
”的方法很简单,在 Pivot 教程中使用。我们在这里只是简化了它!
/**
* Binding WTKX file in Java UI class by using WTKXSerializer
*
* @param obj Java class UI
* @param ui path to WTKX file
* @throws IOException
* @throws SerializationException
*/
public static void bind(Object obj, String ui) throws IOException,
SerializationException
{
WTKXSerializer serializer = new WTKXSerializer();
serializer.readObject( new Linker().resource( ui ) );
serializer.bind(obj);
}
第二个名为“link
”的方法用于将 controllers
链接到“components
”。请注意,我们可以将多个 controllers
链接到一个 component
。
/**
* Linking controllers to Java UI class components
*
* @param obj Java UI Class
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
* @throws XPathExpressionException
* @throws ClassNotFoundException
* @throws SecurityException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws NoSuchFieldException
*/
@SuppressWarnings({ "unchecked" })
public static void link(Object obj) throws ParserConfigurationException,
SAXException, IOException, XPathExpressionException,
ClassNotFoundException, SecurityException, NoSuchMethodException,
IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException, NoSuchFieldException
{
// reading a XML file (configuration file)
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse( new Linker().resource("controllers.xml") );
// creating and parsing configuration file by using XPath
XPathFactory xfactory = XPathFactory.newInstance();
XPath xpath = xfactory.newXPath();
XPathExpression xexpression = xpath.compile("//Configuration/View
[@name='" + obj.getClass().getSimpleName() + "']/Controller");
NodeList result = (NodeList) xexpression.evaluate(document,
XPathConstants.NODESET);
for( int x = 0 ; x < result.getLength() ; x++ )
{
//~ bind controller
//# create a class instance
String fieldName =
result.item( x ).getAttributes().getNamedItem("field").getNodeValue();
String className =
result.item( x ).getAttributes().getNamedItem("class").getNodeValue();
String eventName =
result.item( x ).getAttributes().getNamedItem("event").getNodeValue();
String eventPckg =
result.item( x ).getAttributes().getNamedItem
("eventPackage").getNodeValue();
eventPckg = eventPckg != null && !eventPckg.isEmpty() ?
eventPckg : "org.apache.pivot.wtk";
Object classInstance = null;
{
Class clazz = Class.forName("controller." +
obj.getClass().getSimpleName().toLowerCase() +
"." + className + "Controller");
Constructor ctr = clazz.getConstructor
( new Class[]{Object.class} );
classInstance = ctr.newInstance(new Object[]{ obj });
}
//#
//# getting listeners
//#
// get field example PushButton
Object fieldObject = null ;
{
Field field = obj.getClass().getDeclaredField( fieldName );
field.setAccessible( true );
fieldObject = field.get(obj);
}
// get Listeners List from button
Object lisenter = null;
{
// as "getButtonPressListeners"
Method method = fieldObject.getClass().getMethod
( "get" + eventName + "Listeners" , new Class[]{});
lisenter = method.invoke( fieldObject , new Object[]{});
}
// add new listener to button
{
Method method = lisenter.getClass().getMethod
("add", new Class[]{Object.class} );
method.invoke( lisenter , new Object[]{classInstance} );
}
}
}
– 为什么我们不使用 Spring?如果我们使用它,我们可以做类似的工作!–
待续,“8 – 与魔法物品‘控制器’的链接”。
6. 映射!POJO 和 DAO “模型”
我们在这里不讨论如何配置 Spring 和 Hibernate。您可以下载源代码并查看 applicationContext.xml、hibernate.cfg.xml 和 jdbc.properties 文件。
在我们的例子中,一个 POJO 很容易,我们没有复杂的数据库模式,没有多对多,没有一对多关系,只有单独的表“mob_db
”和“item_db
”。
问:但我们只使用这些列进行演示吗?
答:不!我们只使用有趣的列,例如“ID
”、“Name
”!
问:我们讨论“mobs
”和“items
”,也许它们有类似的功能?
答:是的!正如您在用例中看到的,“mob
”和“item
”具有类似的功能,我们仅以“mobs
”为例进行演示。
问:我们使用什么类型的映射?
答:注解很简单。
问:从文章中理解这一部分很难?
答:哎呀!秘密。
我们创建一个名为“Mob
”的类,该类仅包含感兴趣的列
ID
(例如:1109)Sprite
(例如:DEVIRUCHI)kName
(例如:Deviruchi)LV
(例如:46)HP
(例如:6666)SP
(例如:0)Race
(例如:Demon)
(Deviruchi 是我最喜欢的宠物 (>_<)!? 可爱吗?!)
我们创建一个 DAO 接口,其中包含 2 个方法
列表
edit
public interface MobDAO
{
/**
* Get all mobs (monsters) list
* @return
*/
public List<Mob> getList();
/**
* Edit mob information
* @param mob new values
* @return
*/
public boolean edit( Mob mob );
}
您可以在源代码中看到此接口的实现。
7. 创建 Pivot WTKX “视图”
如果您在这里!并且还没有气馁,您是真的被好奇心所吸引了(^_^),我给您两个消息,您先听“好”的还是“坏”的?!
好的,我选择“坏”消息!目前——我们不能在 Pivot 中作弊,也不能使用任何机器人或人工智能来获取经验——我的意思是目前我们没有语法着色、智能感知、向导、拖放等。您必须使用您最喜欢的文本编辑器完成所有工作。查看 此处。
“好”消息!Pivot UI 非常简单清晰。您可以成为大师,也可以找到别人的想法,然后将其革命化,因为您知道“你想要什么”,不像那些使用 DND(拖放)和向导的人。您想尝试吗?别怕!Pivot 团队已经计划了一个 GUI 生成器,请查看 此链接。
但是我们是作弊者,我们向 WTKX 文件添加了“*.xml”,因为我们在 Eclipse 中有语法着色!
正如我提到的,WTKX 文件位于资源文件夹中,我们有两个文件
- romanager/ui.wtkx.xml 用于
RoManager 视图
- romanager/mobs/ui.wtkx.xml 用于
EditMob 视图
您可以在源代码中发现 RoManager
的 WTKX 文件,但我在这里向您展示 EditMob
WTKX 文件
<Dialog wtkx:id="dialogEditMob" title="Edit Mob!"
xmlns:wtkx="http://pivot.apache.org/wtkx"
xmlns="org.apache.pivot.wtk">
<content>
<BoxPane>
<Form wtkx:id="formEditMobs">
<sections>
<Form.Section>
<Label wtkx:id="labelID" Form.label="ID" textKey="id"/>
<Label wtkx:id="labelSprite" Form.label="Sprite"
textKey="sprite"/>
<TextInput wtkx:id="textName" Form.label="Name"
textKey="name"/>
<TextInput wtkx:id="textLevel" Form.label="Level"
textKey="level"/>
<TextInput wtkx:id="textHP" Form.label="HP" textKey="hp"/>
<TextInput wtkx:id="textSP" Form.label="SP" textKey="sp"/>
<TextInput wtkx:id="textRace" Form.label="Race"
textKey="race"/>
<Separator/>
<Label wtkx:id="labelMessage"/>
</Form.Section>
</sections>
</Form>
<BoxPane>
<PushButton wtkx:id="btnSave" buttonData="Save"/>
<PushButton wtkx:id="btnClose" buttonData="Close"/>
</BoxPane>
</BoxPane>
</content>
</Dialog>
这是 Java UI 类
public class EditMob
{
@WTKX private Dialog dialogEditMob = null;
@WTKX private Form formEditMobs = null;
@WTKX private Label labelID = null;
@WTKX private Label labelSprite = null;
@WTKX private TextInput textName = null;
@WTKX private TextInput textLevel = null;
@WTKX private TextInput textHP = null;
@WTKX private TextInput textSP = null;
@WTKX private TextInput textRace = null;
@WTKX private PushButton btnSave = null;
@WTKX private PushButton btnClose = null;
@WTKX private Label labelMessage = null;
/**
* Default constuctor
* @throws Exception
*/
public EditMob() throws Exception
{
Linker.bind(this, "romanager/mobs/ui.wtkx.xml");
Linker.link( this );
}
public Dialog getDialogEditMob() {
return dialogEditMob;
}
public Form getFormEditMobs() {
return formEditMobs;
}
public Label getLabelID() {
return labelID;
}
public Label getLabelSprite() {
return labelSprite;
}
public TextInput getTextName() {
return textName;
}
public TextInput getTextLevel() {
return textLevel;
}
public TextInput getTextHP() {
return textHP;
}
public TextInput getTextSP() {
return textSP;
}
public TextInput getTextRace() {
return textRace;
}
public PushButton getBtnSave() {
return btnSave;
}
public PushButton getBtnClose() {
return btnClose;
}
public Label getLabelMessage() {
return labelMessage;
}
}
简单吗?
8. 与魔法物品“控制器”的链接
继续……!controllers
已由 Linker
类自动配置,从位于资源文件夹中的名为“controllers.xml”的文件中,我创建了一个 DTD 文件用于智能感知
DTD 文件,一个“linker.dtd”文件源代码
<!ELEMENT Configuration (View*)>
<!ELEMENT View (Controller*)>
<!ELEMENT Controller EMPTY >
<!ATTLIST View name CDATA #REQUIRED>
<!ATTLIST Controller field CDATA #REQUIRED>
<!ATTLIST Controller class CDATA #REQUIRED>
<!ATTLIST Controller event CDATA #REQUIRED>
<!ATTLIST Controller eventPackage CDATA "">
用于创建控制器!对于 Java UI 类中的任何组件,必须实现 Listener
接口,并添加一个带有一个参数的构造函数:Object
,用于传递 View
(Java UI 类)对象,正如您在此处看到的
public class CloseDialogController implements ButtonPressListener
{
/**
* View - Java UI Class
*/
private EditMob view = null ;
public CloseDialogController( Object view )
{
this.view = (EditMob) view;
}
public void buttonPressed(Button button) {
this.view.getDialogEditMob().close();
}
}
最后,只需在“controllers.xml”中添加控制器(在 Spring 中称为 bean),而名称中没有“Controller”,控制器 CloseDialog
就被自动链接(在 Spring 中注入)到其 component
中了
<Controller event="ButtonPress"
class="adrabi.codeproject.controllers.editmob.CloseDialog" field="btnClose"/>
魔法?
这里是完整的 controllers.xml 源代码
<!DOCTYPE Configuration SYSTEM "linker.dtd" >
<Configuration>
<View name="RoManager">
<Controller event="ButtonPress"
class="adrabi.codeproject.controllers.romanager.RefreshMobs"
field="btnRefreshMob"/>
<Controller event="ButtonPress"
class="adrabi.codeproject.controllers.romanager.EditMob"
field="btnEditMob"/>
<Controller event="TableViewSort"
class="adrabi.codeproject.controllers.romanager.MobsSort"
field="tableMobs"/>
<Controller event="TableViewSelection"
class="adrabi.codeproject.controllers.romanager.ShowDetails"
field="tableMobs"/>
<Controller event="ImageView"
class="adrabi.codeproject.controllers.romanager.ImageMobLoad"
field="imageMob"/>
</View>
<View name="EditMob">
<Controller event="ButtonPress"
class="adrabi.codeproject.controllers.editmob.CloseDialog"
field="btnClose"/>
<Controller event="ButtonPress"
class="adrabi.codeproject.controllers.editmob.SaveMob"
field="btnSave"/>
</View>
</Configuration>
“最终任务完成。”
9. 总结
一个巨大的秘密,我是一名 ASP.NET 开发者=)! JavaEE、Spring、Hibernate 对我来说是新的。我开始写这篇文章是为了鼓励自己学习一点 JEE,通过使用宅/尼特风格来让其他对 JEE 感兴趣的新手觉得有趣!现在您知道 Adobe 和 Microsoft 都无法创建 RIA 框架,Apache 也通过 Pivot 加入了 RIA 的竞争。
- Pivot 是 Apache 使用 Java 技术和 XML 创建的 RIA。
- Spring,邪恶的框架,为 Hibernate 等其他框架提供了大量支持。
- Hibernate,一个流行的 Java 持久化/ORM 框架。
去获取源代码并完成“items
”的管理,就像“mobs
”一样!
“游戏结束!”
非常感谢您的阅读,以及您的评论、投票、建议和更正。
10. 历史
- 2010年3月28日:初始版本