write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
讨论新闻组及文件
有的引擎的源代码是越看越吸引人的,越看越让人惊呼设计之精巧,实现之完美的,很遗憾的,Orx的源码不是属于那一类,使用的感觉也不像那一类,反而越看越觉得其设计思想上与主流思想格格不入。。。。。在上一节
中list,tree的设计中,这种感觉达到了顶峰。。。。。但是,对于一个这样跨平台的引擎,我还是会看下去,也许看完美的作品,我只能惊叹,看不完美的作品,我却可以了解到更多值得改进的地方吧。在看Orx的源代码的过程中,起码我是对C语言怎么来组织一个相对规模较大的工程有了一些了解,对于常年使用C++的我,这方面知识其实还是相对欠缺。
Core部分主要包含5个部分,Clock,Config,Event,Locale,System。其中Clock,Config和Event比较在Orx中占很重要的地位,会看的详细点,其他略看。另外,因为Clock依赖于structure部分,会等到看完structure以后再看。
System部分主要是用于获取系统事件,以及delay的,太过简单了,就这一句话了。
Locale虽然有些意思,但是算不是Orx的核心模块,对于对整个Orx的理解没有帮助,这个留在自己需要实现多语言的时候再回来参考吧。
Event
Event在Orx也算是比较核心的内容了,贯穿整个系统,很多功能的使用,比如物理的碰撞处理等都是通过事件响应来完成的。
先看结构:
外部结构:
/*
* Public event structure
*/
typedef
struct
__orxEVENT_t
{
orxEVENT_TYPE eType;/*
*< Event type : 4
*/
orxENUM eID;/*
*< Event ID : 8
*/
orxHANDLE hSender;/*
*< Sender handle : 12
*/
orxHANDLE hRecipient; /*
*< Recipient handle : 16
*/
void
*pstPayload; /*
*< Event payload : 20
*/
} orxEVENT;
内部结构:
/*
**************************************************************************
* Structure declaration *
**************************************************************************
*/
/*
* Static structure
*/
typedef
struct
__orxEVENT_STATIC_t
{
orxU32 u32Flags; /*
*< Control flags
*/
orxHASHTABLE *pstHandlerTable;/*
*< Handler table
*/
} orxEVENT_STATIC;
/*
**************************************************************************
* Module global variable *
**************************************************************************
*/
static
orxEVENT_STATIC sstEvent;
看了结构已经了解的差不多了,内部实现以Orx自己的hashtable为核心,实现快速的查找及事件的发送,外部以orxEVENT
结构的eType
,eID区分事件,pstPayload
用于事件实际结构的存储。
着重看两个实现:
添加事件关注的
orxSTATUS orxFASTCALL orxEvent_AddHandler(orxEVENT_TYPE _eEventType, orxEVENT_HANDLER _pfnEventHandler):
/*
No bank yet?
*/
if
(pstBank == orxNULL)
{
/*
Creates it
*/
pstBank = orxBank_Create(orxEVENT_KU32_HANDLER_BANK_SIZE, sizeof
(orxEVENT_HANDLER), orxBANK_KU32_FLAG_NONE, orxMEMORY_TYPE_MAIN);
/*
Valid?
*/
if
(pstBank != orxNULL)
{
/*
Tries to add it to the table
*/
if
(orxHashTable_Add(sstEvent.pstHandlerTable, _eEventType, pstBank) == orxSTATUS_FAILURE)
{
/*
Deletes bank
*/
orxBank_Delete(pstBank);
pstBank = orxNULL;
}
}
}
从以上代码我惊讶的发现,hashTable保存的不是一个个Handler的值,而直接是每个eventType对应的一个bank。bank的知识参考以前的文章
。
然后直接从bank中分配内存并赋值:
/*
Valid?
*/
if
(pstBank != orxNULL)
{
orxEVENT_HANDLER *ppfnHandler;
/*
Creates a new handler slot
*/
ppfnHandler = (orxEVENT_HANDLER *)orxBank_Allocate(pstBank);
/*
Valid?
*/
if
(ppfnHandler != orxNULL)
{
/*
Updates it
*/
*ppfnHandler = _pfnEventHandler;
/*
Updates result
*/
eResult = orxSTATUS_SUCCESS;
}
}
我开始很奇怪这种使用方式,直接就对分配的内存赋值
然后就不管了。后来想了想,因为bank毕竟是Orx自己的内存管理模块,这里将来对eventHandler的使用也肯定是遍历,那么Orx中就直接省去了对bank分配内存指针的保存,将来直接遍历bank了。。。。。起码现在是这样猜测的。
看Send部分后一定见分晓。
由于Send函数的源代码较短,全部贴出来了:
orxSTATUS orxFASTCALL orxEvent_Send(const
orxEVENT *_pstEvent)
{
orxBANK *pstBank;
orxSTATUS eResult = orxSTATUS_SUCCESS;
/*
Checks
*/
orxASSERT(orxFLAG_TEST(sstEvent.u32Flags, orxEVENT_KU32_STATIC_FLAG_READY));
orxASSERT(_pstEvent != orxNULL);
/*
Gets corresponding bank
*/
pstBank = (orxBANK *)orxHashTable_Get(sstEvent.pstHandlerTable, _pstEvent->eType);
/*
Valid?
*/
if
(pstBank != orxNULL)
{
orxEVENT_HANDLER *ppfnHandler;
/*
For all handler
*/
for
(ppfnHandler = (orxEVENT_HANDLER *)orxBank_GetNext(pstBank, orxNULL);
ppfnHandler != orxNULL;
ppfnHandler = (orxEVENT_HANDLER *)orxBank_GetNext(pstBank, ppfnHandler))
{
/*
Calls handler
*/
if
((*ppfnHandler)(_pstEvent) == orxSTATUS_FAILURE)
{
/*
Updates result
*/
eResult = orxSTATUS_FAILURE;
break
;
}
}
}
/*
Done!
*/
return
eResult;
}
果然如我在Add Handler中看到Orx的操作后的猜测一样,获取bank,然后遍历bank.................虽然这样的确也能够实现功能,虽然这样可以省去一个容器变量用于存储handler,但是个人还是感觉这样的用法类似hack。。。。想象一下,你使用Win32 API的时候,直接遍历内存链是什么效果。。。。。。或者,bank在设计时就考虑了这样的用法吧。。。。即使如此,还是将bank一物两用了,又作为内存分配的缓存容器,同时还是个实际数据的缓存容器。。。。。
Config
对于以Config为驱动的Orx的来说,这应该算是最最核心的模块了,假如说Orx哪个部分我是最希望抽出来以后独立使用的话,估计也就是这个部分了。Orx的Config部分,不仅仅实现了一个普通Windows INI所需要的简单的那一部分,包括section和key=value的读取,还包括section的继承,key的引用,value的随机,vector value的读取等强大功能,很多功能甚至凌驾于Json等通用配置。当时,这样强大的config不是没有代价的,换来的是较为庞大的配置代码。光是orxConfig.c一个文件,代码行数就超过了4K。
还是先看结构:
/*
**************************************************************************
* Structure declaration *
**************************************************************************
*/
/*
* Config value type enum
*/
typedef
enum
__orxCONFIG_VALUE_TYPE_t
{
orxCONFIG_VALUE_TYPE_STRING = 0
,
orxCONFIG_VALUE_TYPE_FLOAT,
orxCONFIG_VALUE_TYPE_S32,
orxCONFIG_VALUE_TYPE_U32,
orxCONFIG_VALUE_TYPE_BOOL,
orxCONFIG_VALUE_TYPE_VECTOR,
orxCONFIG_VALUE_TYPE_NUMBER,
orxCONFIG_VALUE_TYPE_NONE = orxENUM_NONE
} orxCONFIG_VALUE_TYPE;
/*
* Config value structure
*/
typedef
struct
__orxCONFIG_VALUE_t
{
orxSTRING zValue; /*
*< Literal value : 4
*/
orxCONFIG_VALUE_TYPE eType;/*
*< Value type : 8
*/
orxU16 u16Flags; /*
*< Status flags : 10
*/
orxU8 u8ListCounter;/*
*< List counter : 11
*/
orxU8 u8CacheIndex; /*
*< Cache index : 12
*/
union
{
orxVECTOR vValue; /*
*< Vector value : 24
*/
orxBOOL bValue; /*
*< Bool value : 16
*/
orxFLOAT fValue; /*
*< Float value : 16
*/
orxU32 u32Value; /*
*< U32 value : 16
*/
orxS32 s32Value; /*
*< S32 value : 16
*/
};/*
*< Union value : 24
*/
union
{
orxVECTOR vAltValue;/*
*< Alternate vector value : 36
*/
orxBOOL bAltValue;/*
*< Alternate bool value : 28
*/
orxFLOAT fAltValue;/*
*< Alternate float value : 28
*/
orxU32 u32AltValue;/*
*< Alternate U32 value : 28
*/
orxS32 s32AltValue;/*
*< Alternate S32 value : 28
*/
};/*
*< Union value : 36
*/
} orxCONFIG_VALUE;
/*
* Config entry structure
*/
typedef
struct
__orxCONFIG_ENTRY_t
{
orxLINKLIST_NODE stNode; /*
*< List node : 12
*/
orxSTRING zKey; /*
*< Entry key : 16
*/
orxU32 u32ID;/*
*< Key ID (CRC) : 20
*/
orxCONFIG_VALUE stValue;/*
*< Entry value : 56
*/
orxPAD(56
)
} orxCONFIG_ENTRY;
/*
* Config section structure
*/
typedef
struct
__orxCONFIG_SECTION_t
{
orxLINKLIST_NODE stNode; /*
*< List node : 12
*/
orxBANK *pstEntryBank; /*
*< Entry bank : 16
*/
orxSTRING zName;/*
*< Section name : 20
*/
orxU32 u32ID;/*
*< Section ID (CRC) : 24
*/
orxU32 u32ParentID;/*
*< Parent ID (CRC) : 28
*/
orxS32 s32ProtectionCounter; /*
*< Protection counter : 32
*/
orxLINKLIST stEntryList;/*
*< Entry list : 44
*/
orxPAD(44
)
} orxCONFIG_SECTION;
/*
* Config stack entry structure
*/
typedef
struct
__orxCONFIG_STACK_ENTRY_t
{
orxLINKLIST_NODE stNode; /*
*< Linklist node : 12
*/
orxCONFIG_SECTION *pstSection; /*
*< Section : 16
*/
} orxCONFIG_STACK_ENTRY;
/*
* Static structure
*/
typedef
struct
__orxCONFIG_STATIC_t
{
orxBANK *pstSectionBank; /*
*< Section bank
*/
orxCONFIG_SECTION *pstCurrentSection;/*
*< Current working section
*/
orxBANK *pstHistoryBank; /*
*< History bank
*/
orxBANK *pstStackBank; /*
*< Stack bank
*/
orxLINKLIST stStackList;/*
*< Stack list
*/
orxU32 u32Flags; /*
*< Control flags
*/
orxU32 u32LoadCounter; /*
*< Load counter
*/
orxSTRING zEncryptionKey; /*
*< Encryption key
*/
orxU32 u32EncryptionKeySize; /*
*< Encryption key size
*/
orxCHAR *pcEncryptionChar; /*
*< Current encryption char
*/
orxLINKLIST stSectionList;/*
*< Section list
*/
orxHASHTABLE *pstSectionTable;/*
*< Section table
*/
orxCHAR zBaseFile[orxCONFIG_KU32_BASE_FILENAME_LENGTH]; /*
*< Base file name
*/
} orxCONFIG_STATIC;
/*
**************************************************************************
* Static variables *
**************************************************************************
*/
/*
* static data
*/
static
orxCONFIG_STATIC sstConfig;
还是那句话,看结构比看流程实在是更加有用,对于Orx来说,说了它再多的不是,它每个模块的结构之清晰,(虽然每个模块之间关系有点乱)真是很多软件设计根本没法比的。
所有的配置由sstConfig存储。orxHASHTABLE *pstSectionTable; 用于快速的查找section,完整的section信息保存在stSectionList
中。
每个orxCONFIG_SECTION
结构本身又包含stEntryList
表示的list,存储的是orxCONFIG_ENTRY
结构,表示一组key=value对。zKey
自然表示完整的key,u32ID
表示CRC过的key。value保存在orxCONFIG_VALUE结构的
成员变量stValue
中。
orxCONFIG_VALUE
就是很多时候能在C/C++语言中看到的万能结构,用于保存一个可能为各种类型的变量。
zValue用于保存原始的value字符串,然后eType表示类型,具体的value由此type决定,保存在下面两个union中。
出去一些辅助成员,(比如stack,CurrentSection
等用于暂时缓存现在config操作的成员变量),主要的config变量是多层容器结构。
由config保存着section容器列表,每个section容器又包含entry容器列表,entry容器保存key=value对。
其实看到结构已经对Orx的Config了解的很清楚了,但是回头想想,对于类似的需求,真的也不会以其他方式实现,还不就是上层的东西包含一个下层东西的列表啊。
另外,section结构中是包含一个u32ParentID变量的,用于表示继承自哪个父section。
没有在结构中看到Orx拥有的引用,随机等内容,说明这些都是都是在解析config的时候搞定的。
下面只看一个函数orxConfig_Load,搞定这一个,基本其他也不用看了。
主要解析config的过程是下列这样一个for循环结构
for
(u32Size = orxFile_Read(acBuffer, sizeof
(orxCHAR), orxCONFIG_KU32_BUFFER_SIZE, pstFile), u32Offset = 0
, bFirstTime = orxTRUE;
u32Size > 0
;
u32Size = orxFile_Read(acBuffer + u32Offset, sizeof
(orxCHAR), orxCONFIG_KU32_BUFFER_SIZE - u32Offset, pstFile) + u32Offset, bFirstTime = orxFALSE)
{
.............
很夸张的for运用方式。。。。。。。。但是理解上没有什么问题,也就是遍历读取文件到一个叫acBuffer的buffer中。
然后遍历buffer中的每个字符开始解析:
每一行的解析直到这个时候为止:
/*
Comment character or EOL out of block mode or block character in block mode?
*/
if
((((*pc == orxCONFIG_KC_COMMENT)
|| (*pc == orxCHAR_CR)
|| (*pc == orxCHAR_LF))
&& (bBlockMode == orxFALSE))
|| ((bBlockMode != orxFALSE)
&& (*pc == orxCONFIG_KC_BLOCK)))
{
............
上面也就是找到了行结尾了。
当发现当前行有section的时候(通过[]来判断)
用 orxConfig_SelectSection(pcLineStart + 1);来完成section的创建及选择(假如已经创建了的话)
这样的意义在于无论多少个section,最后都会拼接成一个,并且实现Orx配置中后面出现的配置会覆盖前面的配置的特性。
static orxINLINE orxCONFIG_SECTION *orxConfig_CreateSection(const orxSTRING _zSectionName, orxU32 _u32SectionID, orxU32 _u32ParentID)
中有两句关键的代码:
orxLinkList_AddEnd(&(sstConfig.stSectionList), &(pstSection->stNode));
/* Adds it to table */
orxHashTable_Add(sstConfig.pstSectionTable, _u32SectionID, pstSection);
以此完成section的添加,如看结构的时候分析的一样,分别添加进list和hashTable中。
而当发现当前行有key和value的时候:
if((pcKeyEnd != orxNULL) && (pcValueStart != orxNULL))
处理一下数据,通过orxConfig_AddEntry函数,添加一个入口。
AddEntry中会复制key和value到config中,
其中orxConfig_ComputeWorkingValue函数中处理了value的引用,继承,随机等问题。当然,其实也没有什么技术含量,实际也就是对value进行类似上面的字符解析而已。
然后用以下语句解答了结构中的分析。
orxLinkList_AddEnd(&(sstConfig.pstCurrentSection->stEntryList), &(pstEntry->stNode));
以上代码对当前选中的section的EntryList中添加了新的entry节点,也就是表示当前的key=value对。
以上代码基本已经完成了从section列表的创建到entry列表创建。
小结
其实,开始的时候我以为中间包含的字符解析,文件包含,继承,引用等内容,还有些看头,后来看了才发现,由于都用了很特殊的字符来表示,实际也就是找到相关的字符,然后处理的过程,没有啥技术含量。。。。。。。。。大失所望。。。。。。不过回头来想想,Martin Fowler说,傻子都会写让计算机理解的代码;而优秀程序员写的是人能看懂的代码。从这个角度看,config写的是很成功的了。。。。。只是抱着商业大片的人结果看的是平淡无奇的文艺片,可能有些失望吧。
原创文章作者保留版权 转载请注明原作者 并给出链接
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
分享到:
相关推荐
非典型2D游戏引擎 Orx 源码 收集完整的 源码包
ORX源码,这是学习2D游戏引擎必不可少的一道大餐
Orx是一款轻便,轻巧,基于插件,数据驱动且非常易于使用的面向2D的游戏引擎。 由于orx是数据驱动的,因此它允许用户使用少量的代码行来创建游戏,并大大减少了开发时间。
是“一个开源,可移植,轻便,基于插件,数据驱动且非常易于使用的2D导向游戏引擎。” Orx本身是用C编写的。 该存储库提供绑定以使用OCaml中的Orx。 声音,图形,物理,输入处理等等可以由Orx用C语言处理,而游戏...
Orx( )是一个开源的,可移植的,轻量级的,基于插件的,数据驱动的并且非常易于使用的面向2D的游戏引擎。 Orx为游戏开发提供了完整的框架,目前可在Windows(mingw和使用Visual Studio的本机),Linux(x86 / x86...
C#实现类似淘宝图片局部放大功能源码,测试能用
包装器包括两个部分: 低层包装器,每个ORX C标头基本上具有一个Nim模块,其中将近80个。 所有这些都被命名为o-xxx,例如oinput或oobject 。 每个低级包装器具有一个Nim模块的高级包装器。 当前,每个高级包装器也...
Suz-OrX-archive-refs-heads-master.zip
用法将html-colors.ini复制到您的.ini文件夹(通常为{project-root}/data/config并将其包含在主.ini文件中。 包含之后,您可以通过@Colors.MediumOrchid直接引用颜色名称。 @html-colors.ini@[Object]Color = @...
2017年java源码java-Order-System-Utility ORX Build 2.0 Beta ORX是一个简单,可移植但功能有限的订购系统。 Jentzen Paolo Ancheta Javier版权所有(C)2017 ORX绝对不提供保修。 这是一个免费软件,出于教育目的...
什么是ORx? ORx的名称为“ Oh-Rex”,代表Outernet ReceiverX。“ X”代表自制设备。支持的设备和配置请注意,Raspberry Pi v1是目前唯一受支持的版本。 v2具有不支持的ARM v7处理器。 此存储库中的脚本支持以下...
TexturePacker 4.3.1 x64、X86完美破解,破解步骤简单,已经验证完美破解可用
rewrite 2.0 32位 64位,方便大家安装IIS的URL重写环境
htop工具安装源码; Htop是Linux系统中的一个互动的进程查看器,一个文本模式的应用程序(在控制台orX终端中),需要ncurses。与Linux传统的top相比,htop更加人性化。它可以让用户交互式操作,支持颜色主题,可横向...
Risk Taxonomy Risk Taxonomy Risk Taxonomy
(2)进入 U-Boot源码目录 #cd u-boot-1.3.1 (3)创建自己的开发板: #cd board #cp smdk2410 fs2410 –a #cd fs2410 #mv smdk2410.c fs2410.c #vi Makefile (将 smdk2410修改为 fs2410) #cd ../../...
win2008以上的服务器IIS设置URL规则组件,伪静态规则设置
matlab中拟合中心线的代码关于OrX程序的注意事项: 最初由SR Kiihne撰写,2004年2月24日 定向的MAS nmr光谱 我最初是在来自Bruker实验的光谱上对此进行测试的:jr0203#22-26这些是31P MAS光谱,其自旋速率为500-...
20-向脚本传递参数.pdf shell编程
Pa Pb RS功率计算de-boosting计算工具