minetest源码解析五:IGameDef、ItemDefManager、NodeDefManager类介绍
minetest源码解析五:IGameDef、ItemDefManager、NodeDefManager类介绍
IGameDef
minetest->gamedef.h
这个类中主要管理了游戏全局的一些定义。通过这个类接口可以获取它所有管理的对象指针。主要包括了TextureSource、ShaderSource、ItemDefManager、NodeDefManager、CraftDefManager、SoundManager、MtEventManager、RollbackReportSink。
这个对象的实例会发现在这个程序中作为参数被传入很多对象中,譬如在一些对象初始化时就传入。
这个类中的接口没有都实现,且它是一个基类,主要是在Client和Server这两个类中实现的,这两个类都继承了IGameDef。SoundManager、MtEventManager主要在client类中会使用到;RollbackReportSink主要在server这个类中会使用到。
相关涉及类
minetest->server.h
class Server : public con::PeerHandler, public MapEventReceiver, public InventoryManager, public IGameDef
minetest->client.h
class Client : public con::PeerHandler, public InventoryManager, public IGameDef
创建这个对象的位置
game.cpp->the_game()
server = new Server(map_dir, gamespec, simple_singleplayer_mode,bind_addr.isIPv6());
Client client(device, playername.c_str(), password, draw_control, tsrc, shsrc, itemdef, nodedef, sound, &eventmgr, connect_address.isIPv6());
IGameDef *gamedef = &client;
ItemDefManager
minetest->itemdef.h
这个对象中管理了所有的Item,这里说的Item是指ItemType是none、node、craft、tool这几类的。这里的所有item都能在物品栏中显示出来。涉及到的注册方法时如下几种:
minetest.register_item type = "none",
core.register_node nodedef.type = "node"
core.register_craftitem craftitemdef.type = "craft"
core.register_tool tooldef.type = "tool"
node 、craftitem、tool 都在物品栏表内,可以在游戏界面下方显示,有些type在游戏上是不能放置,只能使用的。
注意:如下这种注册craft这种不包括在item中。
minetest.register_craft({
output = 'bucket:bucket_empty 1',
recipe = {
{'default:steel_ingot', '', 'default:steel_ingot'},
{'', 'default:steel_ingot', ''},
}
})
ItemDefManager对象中重要的成员变量m_item_definitions以及m_aliases。
std::map<std::string, ItemDefinition*> m_item_definitions;
// Aliases
std::map<std::string, std::string> m_aliases;
m_item_definitions 是注册时加入的一系列items。m_aliases是一些item的名字不是一个,为了兼容之前的名字,譬如minetest.register_alias("stone", "default:stone”)。
ItemDefinition(minetest->itemdef.h) 是结构体,描述定义item,其中有个ItemType type,这个表示的是item的类型,只有ITEM_NONE,ITEM_NODE,ITEM_CRAFT,ITEM_TOOL这四个值。
NodeDefManager
minetest->nodedef.h
这个对象主要管理了所有的node,必须是itemType是node类型的,注册的方式时通过core.register_node这种方式的。
NodeDefManager对象中重要的成员变量m_content_features。
// Features indexed by id
std::vector<ContentFeatures> m_content_features;
m_content_features是一系列node。
ContentFeatures(minetest->node.h)是结构体,这里主要是node的一些特性,譬如tiles、name、pointable(玩家能指向它)、diggable(玩家能挖掉它)、liquid_type、rightclickable、selection_box、damage_per_second等等这些。其中有个NodeDrawType drawtype,这里代表有14种node类型,每种类型显示的方式时不一样的,具体类型如下NDT_NORMAL, NDT_AIRLIKE, NDT_LIQUID,NDT_FLOWINGLIQUID,NDT_GLASSLIKE, NDT_ALLFACES, NDT_ALLFACES_OPTIONAL, NDT_TORCHLIKE, NDT_SIGNLIKE, NDT_PLANTLIKE, NDT_FENCELIKE, NDT_RAILLIKE, NDT_NODEBOX, NDT_GLASSLIKE_FRAMED
ItemDefManager、NodeDefManager读入值流程图
流程图中核心API说明
1.the_game():创建服务器
minetest->game.cpp->the_game()
server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr.isIPv6());
2.Initialize scripting
minetest->server.cpp
m_script = new GameScripting(this);
初始化过程中注册了一系列函数,lua文件中调用C函数。譬如注册了lua文件中的register_item_raw对应调用的C函数是l_register_item_raw()。
主要操作流程是new Server()->new GameScripting()->GameScripting::InitializeModApi(L, top)->ModApiItemMod::Initialize(L, top)->ModApiBase::registerFunction()
涉及到的函数
minetest->script->scripting_game.cpp
GameScripting::GameScripting(Server* server)
{
…
// Initialize our lua_api modules
InitializeModApi(L, top);
…
}
void GameScripting::InitializeModApi(lua_State *L, int top)
{
// Initialize mod api modules
…
ModApiItemMod::Initialize(L, top);
…
}
minetest->script->lua_api->l_item.cpp
void ModApiItemMod::Initialize(lua_State *L, int top)
{
API_FCT(register_item_raw);
API_FCT(register_alias_raw);
API_FCT(get_content_id);
API_FCT(get_name_from_content_id);
}
register_item_raw 主要是core.register_item中调用
minetest->script->lua_api->l_internal.h
#define API_FCT(name) registerFunction(L, #name, l_##name,top)
minetest->script->lua_api->l_base.cpp
bool ModApiBase::registerFunction(lua_State *L, const char *name, lua_CFunction fct, int top)
{
//TODO check presence first!
lua_pushstring(L,name);
lua_pushcfunction(L,fct);
lua_settable(L, top);
。。。
}
3.加载脚本m_script->loadScript(scriptpath)
minetest->script->cpp_api->s_base.cpp->bool ScriptApiBase::loadScript(const std::string &scriptpath)->luaL_loadfile(L, scriptpath.c_str()) || lua_pcall(L, 0, 0, m_errorhandler)
执行这个路径下的lua文件 */MineT.app/Contents/Resources/share/builtin/init.lua ,init.lua会相应调用game文件夹中的init.lua,从而会调用到 */MineT.app/Contents/Resources/share/builtin/game/register.lua。
以下是这个文件register.lua中的部分代码,可以知道加载这个文件时会执行register_item_raw(itemdef),从而调用在脚本初始化时注册的register_item_raw对应的C函数是l_register_item_raw。
function core.register_item(name, itemdef)
。。。
register_item_raw(itemdef)
end
core.register_item(":unknown", {
type = "none",
description = "Unknown Item",
inventory_image = "unknown_item.png",
on_place = core.item_place,
on_drop = core.item_drop,
groups = {not_in_creative_inventory=1},
diggable = true,
})
此时会加载这里的4个item。
4.调用C函数l_register_item_raw
minetest->script->lua_api->l_item.cpp->ModApiItemMod::l_register_item_raw(lua_State *L)
获取服务端的ItemDefManager以及INodeDefManager指针,从栈中读取item definition值并设置值。
涉及函数
// Register item definition
idef->registerItem(def);
// Read the node definition (content features) and register it
if(def.type == ITEM_NODE)
{
ContentFeatures f = read_content_features(L, table);
content_t id = ndef->set(f.name, f);
}
5.加载脚本m_script->loadMod()
minetest->script->cpp_api->s_base.cpp
bool ScriptApiBase::loadMod(const std::string &scriptpath, const std::string &modname)
检查mod 的name并加载mod中的init文件,如init.lua内容如下,代表注册了一个node,并按照函数要求传入了一些参数。函数定义是在builtin/game/register.lua中的core.register_node。执行init.lua文件,执行到minetest.register_node,等于去执行builtin/game/register.lua中的core.register_node,然后通过core.register_item执行register_item_raw,从而执行对应C函数完成item数据读入的。
minetest.register_node("tutorial:decowood", {
description = "decowood",
tiles = {"tutorial_decowood.png"},
groups = {snappy=1,choppy=2,oddly_breakable_by_hand=2,flammable=3},
})
function core.register_node(name, nodedef)
nodedef.type = "node"
core.register_item(name, nodedef)
end
function core.register_item(name, itemdef)
。。。
register_item_raw(itemdef)
end