战魂小筑

跨平台通用游戏服务器架构, 脚本及嵌入技术, 3D引擎&Shader代码生成, 开发工具技巧 email: 20998333#qq.com

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  169 随笔 :: 0 文章 :: 328 评论 :: 0 Trackbacks

2012年5月12日 #

最近团队已经全面弃用SVN全面转移到HG(Mercurial)分布式代码管理

Visual Studio系编辑工具由于质量过硬, 兼容性超强,因此使用HG一直没有出现过问题

但是第三方开发的FlashDevelop对UNICODE兼容性不好, 出现了很多乱码问题, 特别是在HG合并代码后, 代码中的中文在FlashDevelop中的乱码现象更是严重. 经过验证, 同样代码在FlashBuilder中没有问题. 因此可以推断是FlashDevelop兼容性问题

为解决此问题, 需要调整HG的配置, 这里以Windows环境下的TortoiseHg为例

解决方案如下:

kdiff3是TortoiseHg的默认合并工具, 默认安装路径在c:\Program Files\TortoiseHg\kdiff3.exe, 找到并运行

在菜单中找到Settings->Configure KDiff3...

在Regional Settings选项卡中选择Unicode, 8 bit(UTF-8) 然后取消勾选右边的Auto Detect Unicode

image

注意, 如果需要混合开发UTF8保存的源码和UNICODE保存的源码, 需要准备两套合并方案, 可以选择兼容性比较好的BeyondCompare

posted @ 2012-05-12 10:17 战魂小筑 阅读(605) | 评论 (0)编辑 收藏

2012年4月26日 #

传统发布现状

传统的服务器/客户端版本发布流程都需要经历以下流程:

1. 获取代码

2. 编译代码

3. 将配置,二进制文件, 资源打包

4. 挂接远程服务器磁盘拷贝打包文件

5. 远程操作解压打包文件

6. 修改设置,指向最新版本

7. 重启服务器

此流程繁琐,重复且无聊, 同时, 由于网络带宽,网速等约束, 每次若使用完整包发布,传输起来非常吃力

本文讨论的外网服务器由于安全性要求,禁止root登录,只能用普通帐号登录或传输后, 提权为root继续进行以上操作, 因此rsync的使用受到严重限制

即便使用Windows下的同步软件, 也几乎不可能.

HG特性及优势

HG作为一个优秀,小巧的跨平台代码管理软件的特性,正好能解决以上问题, 主要特性:

1. 安装简便, 可以使用代码直接安装

2. 利用本地映射版本可以对新版本做差异比较

3. 增量包传输, 100%同步, 本地文件删除后, 远程文件也会同步删除

4. 传输压缩

5. 增量包可以打包为patch进行离线更新

6. 可以恢复到任意版本, 提交版本有据可查

 

以下部署系统以CentOS为基础, 其他系统类似

本文来自战魂小筑的博客http://www.cppblog.com/sunicdavy 转载请注明来源

为远程服务器安装HG

安装依赖库

yum install python-devel

获取HG源码

wget http://mercurial.selenic.com/release/mercurial-2.1.tar.gz

tar zxvf ./mercurial-2.1.tar.gz

编译安装

make all

make install

hg debuginstall

 

使用HG同步数据

创建仓库

找到你需要同步的目录,进入目录

执行

hg init

vi .hg/hgrc

添加以下内容,让这个仓库支持外部push

[ui]

username=服务器提交后看到的用户名

[web]

push_ssl = false
allow_push=*

同步

vi /etc/sysconfig/iptables

添加HG服务的8000端口

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 8000 -j ACCEPT

开启仓库同步服务

hg serve

本地机器同样找到文件夹,创建仓库

hg init

以后每次需要同步时,使用命令,或者乌龟HG的界面工具拉取服务器数据即可

hg pull http://服务器地址:8000

 

版本提交方法与HG日常使用类似, 这里不再阐述

离线更新

对于某些服务器深处防火墙或者安全登录后方,不能直接开启8000端口的情况

可以使用hg导出一个patch, 传输到远程服务器, 使用hg import PATCH 即可

posted @ 2012-04-26 11:11 战魂小筑 阅读(1232) | 评论 (0)编辑 收藏

2012年4月21日 #

BasicLayout.cpp:37:5: error: 'auto_ptr' in namespace 'std' does not name a type

在包含中添加#include <memory>即可

posted @ 2012-04-21 16:43 战魂小筑 阅读(1199) | 评论 (2)编辑 收藏

2012年4月20日 #

由于服务器需要静态链接所有库,但mysql++默认编译使用的是共享库, 因此使用ar手动生成一个

mysql++版本3.1.0

进入mysql++目录,执行以下指令生成libmysqlpp.a

ar rcu libmysqlpp.a mysqlpp_beemutex.o mysqlpp_cmdline.o mysqlpp_connection.o mysqlpp_cpool.o mysqlpp_datetime.o mysqlpp_dbdriver.o mysqlpp_field_names.o mysqlpp_field_types.o mysqlpp_manip.o mysqlpp_myset.o mysqlpp_mysql++.o mysqlpp_mystring.o mysqlpp_null.o mysqlpp_options.o mysqlpp_qparms.o mysqlpp_query.o mysqlpp_result.o mysqlpp_row.o mysqlpp_scopedconnection.o mysqlpp_sql_buffer.o mysqlpp_sqlstream.o mysqlpp_ssqls2.o mysqlpp_stadapter.o mysqlpp_tcp_connection.o mysqlpp_transaction.o mysqlpp_type_info.o mysqlpp_uds_connection.o mysqlpp_utility.o mysqlpp_vallist.o mysqlpp_wnp_connection.o ssqls2parse_parsev2.o

然后mv到usr/local/lib,重新链接服务器即可

posted @ 2012-04-20 14:48 战魂小筑 阅读(1062) | 评论 (0)编辑 收藏

最近页游开放平台比较多, 每个平台要求的Linux版本各不相同, 这给开发人员部署服务器带来了很大的困难. 在本机Linux编译的程序,发布时即便将依赖的so附带到目标Linux环境,仍然会碰到依赖及版本问题,例如:

[root@localhost bin]# ldd wkcenter
./wkcenter: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.11' not found (required by ./wkcenter)
./wkcenter: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by ./wkcenter)
./wkcenter: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.9' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.9' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.7' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.8' not found (required by ./wkcenter)
./wkcenter: /lib/libc.so.6: version `GLIBC_2.11' not found (required by ./wkcenter)

        linux-gate.so.1 =>  (0xffffe000)
        liblog4cpp.so.4 => not found
        libprotobuf.so.7 => not found
        libboost_filesystem.so.1.48.0 => not found
        libboost_system.so.1.48.0 => not found
        libboost_thread.so.1.48.0 => not found
        libboost_program_options.so.1.48.0 => not found
        libunwind-x86.so.7 => not found
        libluabind.so.0.9.0 => not found
        libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x008ae000)
        libm.so.6 => /lib/libm.so.6 (0x0044b000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00476000)
        libc.so.6 => /lib/libc.so.6 (0x002c1000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x0041d000)
        librt.so.1 => /lib/librt.so.1 (0x00440000)
        /lib/ld-linux.so.2 (0x002a2000)

上面红字部分表示glibc及glibcxx库依赖不正确. 本人使用的Linux编译版本为Mint 11(基于Ubuntu), 一般Ubuntu发行版的glibc配备非常高. 但是上文中的发布的Linux版本为CentOS 5.8

使用/lib/libc.so.6 查看libc版本为2.5, 远远低于开发环境的2.11

GNU C Library stable release version 2.5, by Roland McGrath et al.
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.1.2 20080704 (Red Hat 4.1.2-51).
Compiled on a Linux 2.6.9 system on 2012-02-21.
Available extensions:
        The C stubs add-on version 2.1.2.
        crypt add-on version 2.1 by Michael Glad and others
        GNU Libidn by Simon Josefsson
        GNU libio by Per Bothner
        NIS(YP)/NIS+ NSS modules 0.19 by Thorsten Kukuk
        Native POSIX Threads Library by Ulrich Drepper et al
        BIND-8.2.3-T5B
        RT using linux kernel aio
Thread-local storage support included.
For bug reporting instructions, please see:
<
http://www.gnu.org/software/libc/bugs.html>.

由于Linux操作系统的特有elf加载顺序. (可以参考此文). 虽然可以很大程度上解决Windows早期版本的dll hell问题, 但是给部署带来了很大难度

一般常见的解决方法是, 找到一个与目标Linux版本及glibc版本一致的Linux, 将代码及依赖包放在之上编译, 完成后再发布.这种方法与Linux下常见软件安装方法类似. 但是对于商用服务器部署步骤来说未免繁琐, 安全性低.

还有一种方法,使用静态链接. 将所有可执行文件文件依赖的静态库, 系统库,全部静态链接到可执行文件中,可以一次性解决这个问题

步骤:

    1. 在gcc链接命令行中添加-static -static-libgcc -static-libstdc++

    2. 将第三方依赖库打开静态链接开关, 将原来链接.so的库,全改为链接.a

    3. gcc对链接库顺序很敏感, 链接库顺序需要按照从前至后为:  项目产生的静态库 > 第三方库静态库 > 系统静态库

    4. 链接时, 若有未解决的symbol, 可以尝试在最后添加-lpthread及-lrt解决

   

在发布版本Linux上运行可能遇到的问题:

terminate called after throwing an instance of 'std::runtime_error'

what(): locale::facet::_S_create_c_locale name not valid

解决方法: 执行之前运行export LC_ALL="C"

posted @ 2012-04-20 11:35 战魂小筑 阅读(1323) | 评论 (2)编辑 收藏

2012年4月12日 #

#!/bin/bash
# chkconfig: 3 3 1
# description: svclauncher
ServicePath=/usr/local/bin

ServiceList=(
"wkcenterd --toc /home/davy/dev/kaze/Config/CenterService.toc --logfile /tmp/centerd.log"
"wkagentd --toc /home/davy/dev/kaze/Config/AgentService.toc --logfile /tmp/agentd.log"
)

StartAll()
{
    for((i = 0;i<${#ServiceList[*]};i=i+1))
    do

     echo "start:" $ServicePath/${ServiceList[i]}
     $ServicePath/${ServiceList[i]} > /dev/null &

    done
}

StopAll()
{
    for((i = 0;i<${#ServiceList[*]};i=i+1))
    do

     echo "stop:" $ServicePath/${ServiceList[i]}
     svcname=`echo ${ServiceList[i]} | awk '{print $1}'`
     killall $svcname > /dev/null

    done
}

RestartAll()
{
    StopAll
    StartAll
}


InstallService()
{
    svcname=`basename $0`
    chmod +x $svcname
    cp $svcname /etc/init.d
    ln /etc/init.d/$svcname /etc/rc3.d/S03$svcname
    ln /etc/init.d/$svcname /etc/rc0.d/K03$svcname
    chkconfig --add $svcname
    chkconfig $svcname on
    chkconfig --list | grep $svcname
}

UninstallService()
{
    svcname=`basename $0`
    chkconfig --del $svcname
    rm -f /etc/init.d/$svcname
    rm -f /etc/rc3.d/S03$svcname
    rm -f /etc/rc3.d/K03$svcname
}



case "$1" in
    start)
    StartAll
    ;;
    stop)
    StopAll
    ;;
    restart)
    RestartAll
    ;;
    install)
    InstallService
    ;;
    uninstall)
    UninstallService
    ;;
    *)
           echo "Usage: service $EXEC {install|start|stop|restart|uninst}"
       exit 1
esac
 
exit $? 

posted @ 2012-04-12 09:33 战魂小筑 阅读(923) | 评论 (0)编辑 收藏

2012年4月3日 #

用作团队编码标准很不错

 

态度篇
1. 做实事
不要抱怨,发牢骚,指责他人,找出问题所在,想办法解决。对问题和错误,要勇于承担。
2. 欲速则不达
用小聪明、权宜之计解决问题,求快而不顾代码质量,会给项目留下要命的死角。
3. 对事不对人
就事论事,明智、真诚、虚心地讨论问题,提出创新方案。
4. 排除万难,奋勇前进
勇气往往是克服困难的唯一方法。
学习篇
5. 跟踪变化
新技术层出不穷并不可怕。坚持学习新技术,读书,读技术杂志,参加技术活动,与人交流。要多理解新词背后的所以然,把握技术大趋势,将新技术用于产品开发要谨慎。
6. 对团队投资
打造学习型团队,不断提高兄弟们的平均水平。
7. 懂得丢弃
老的套路和技术,该丢,就得丢。不要固步自封。
8. 打破砂锅问到底
不断追问,真正搞懂问题的本质。为什么?应该成为你的口头禅。
9. 把握开发节奏
控制好时间,养成好习惯,不要加班。

开发流程篇
10. 让客户做决定
让用户在现场,倾听他们的声音,对业务最重要的决策应该让他们说了算。
11. 让设计指导而不是操纵开发
设计是前进的地图,它指引的是方向,而不是目的本身。设计的详略程度应该适当。
12. 合理地使用技术
根据需要而不是其他因素选择技术。对各种技术方案进行严格地追问,真诚面对各种问题。
13. 让应用随时都可以发布
通过善用持续集成和版本管理,你应该随时都能够编译、运行甚至部署应用。
14. 提早集成,频繁集成
集成有风险,要尽早尽量多地集成。
15. 提早实现自动化部署
16. 使用演示获得频繁反馈
17. 使用短迭代,增量发布
18. 固定价格就意味着背叛承诺
估算应该基于实际的工作不断变化。
用户篇
19. 守护天使
自动化单元测试是你的守护天使。
20. 先用它再实现它
测试驱动开发其实是一种设计工具。
21. 不同环境,就有不同问题
要重视多平台问题。
22. 自动验收测试
23. 度量真实的进度
在工作量估算上,不要自欺欺人。
24. 倾听用户的声音
每一声抱怨都隐藏着宝贵的真理。

编程篇
25. 代码要清晰地表达意图

代码是给人读的,不要耍小聪明。
26. 用代码沟通
注释的艺术。
27. 动态地进行取舍

记住,没有最佳解决方案。各种目标不可能面面俱到,关注对用户重要的需求。
28. 增量式编程
写一点代码就构建、测试、重构、休息。让代码干净利落。
29. 尽量简单
宁简勿繁。如果没有充足的理由,就不要使用什么模式、原则和特别的技术。
30. 编写内聚的代码
类和组件应该足够小,任务单一。
31. 告知,不要询问
多用消息传递,少用函数调用。
32. 根据契约进行替换
委托往往优于继承。

调试篇
33. 记录问题解决日志
不要在同一地方摔倒两次。错误是最宝贵的财富。
34. 警告就是错误
忽视编译器的警告可能铸成大错。
35. 对问题各个击破
分而治之是计算机科学中最重要的思想之一。但是,要从设计和原型阶段就考虑各部分应该能够很好地分离。
36. 报告所有的异常
37. 提供有用的错误信息
稍微多花一点心思,出错的时候,将给你带来极大便利。

团队协作篇
38. 定期安排会面时间
常开会,开短会。
39. 架构师必须写代码

不写代码的架构师不是好架构师。好的设计都来自实际编程。编程可以带来深入的理解。
40. 实行代码集体所有制
让开发人员在系统不同区域中不同的模块和任务之间轮岗。
41. 成为指导者
教学相长。分享能提高团队的总体能力。
42. 让大家自己想办法

指引方向,而不是直接提供解决方案。让每个人都有机会在干中学习。
43. 准备好后再共享代码
不要提交无法编译或者没有通过单元测试的代码!
44. 做代码复查
复查对提高代码质量、减少错误极为重要。
45. 及时通报进展与问题

主动通报,不要让别人来问你。

posted @ 2012-04-03 21:40 战魂小筑 阅读(142) | 评论 (0)编辑 收藏

2012年3月30日 #

CodeLite极力的模仿Visual Studio的界面及表现形式. 但是在有些配置项上却存在着操作不同, 看图说话:

image

这是项目中的工程配置设定项中的调试器参数设置. 图中将Program启动程序项设定为相对路径, 而Working Folder工作目录项设为绝对路径.

这样的设定无法启动调试器, gdb将报错, 无法找到调试程序:

image

研究了一下, 发现设定项里引导你使用浏览目录对话框进行选择, 尝试一下, 将Program改为绝对路径

image

F5开始调试, 不报错, main中使用getcwd获取当前路径验证, 设置正确, 问题解决

 

分析: CodeLite应该只是将两个参数简单的传给了gdb, 但是gdb并不知道工程相对路径,因此报错. 这对于CodeLite开发者来说,理解是正确的, 程序员思想.

但是对于产品来说是失败的

VisualStudio的调试器与IDE结合紧密, 因此以产品思想开发程序, 就能避免这种类似的问题

posted @ 2012-03-30 16:07 战魂小筑 阅读(837) | 评论 (0)编辑 收藏

2012年3月27日 #

在lua中, #操作符用于获取对象大小, 对于table来说, 获取的是table元素个数, 对于字符串来说获取的是字符串长度

另外一种获取方法是table.getn(obj), 但是这个方法已经标记为废除了, 尽量使用通用且简洁的#操作符

 

使用lua api实现此功能就需要用到lua_objlen( ),但是这个功能未在luabind中提供.所以我们顺手添加一个

首先找到luabind源码的object.hpp中取对象类型的type函数,在其下添加以下代码

 

  1: template<class ValueWrapper>
  2: inline int obj_size(ValueWrapper const& value)
  3: {
  4:     lua_State* interpreter = value_wrapper_traits<ValueWrapper>::interpreter(
  5:         value
  6:         );
  7: 
  8:     value_wrapper_traits<ValueWrapper>::unwrap(interpreter, value);
  9:     detail::stack_pop pop(interpreter, 1);
 10:     return lua_objlen(interpreter, -1);
 11: }

 

重新编译你的代码, 就可以这样使用luabind::obj_size( obj ) 获取对象大小了
posted @ 2012-03-27 17:36 战魂小筑 阅读(873) | 评论 (0)编辑 收藏

以下代码使用luabind进行lua的coroutine测试

   1: void ScriptManagedChannel::OnServiceInitialize()
   2: {    
   3:     try
   4:     {        
   5:         mThread = lua_newthread( GScriptScriptContext->GetVM() );
   6:  
   7:         luabind::resume_function<void>( mThread, "ScriptMain", this );
   8:  
   9:         Resume();
  10:     }
  11:     catch (std::exception& e)
  12:     {
  13:         const char* ErrorMsg = lua_tostring( GScriptScriptContext->GetVM(), -1 );            
  14:         printf("%s\n", e.what() );
  15:     }
  16:  
  17:     
  18: }
  19:  
  20: void ScriptManagedChannel::Resume( )
  21: {
  22:     luabind::resume<void>( mThread );
  23: }
  24:  
  25: void ScriptManagedChannel::StopTest( )
  26: {
  27:     lua_yield( mThread, 0 );
  28: }
  29:  
  30:  

代码中, mThread类型为lua_State*类型

GScriptScriptContext->GetVM()是加载了代码的lua_State*

StopTest为注册为ScriptManagedChannel类成员函数到lua中的定义

接下来看lua端的测试代码:

   1: function ScriptMain( Channel )
   2:  
   3:     
   4:     for i = 1, 5 do
   5:     
   6:     print("done", i)
   7:     
   8:     Channel:StopTest( )
   9:     
  10:     
  11:     
  12:     end
  13: end

刚开始,在测试代码时, lua中有个手误而造成的错误, 导致C++代码运行到第7行时弹出assert

位于:luabind-0.9.1\luabind\detail\call_function.hpp 第264行,对应以下代码第13行

   1: ~proxy_function_void_caller()
   2: {
   3:     if (m_called) return;
   4:  
   5:     m_called = true;
   6:     lua_State* L = m_state;
   7:  
   8:     int top = lua_gettop(L);
   9:  
  10:     push_args_from_tuple<1>::apply(L, m_args);
  11:     if (m_fun(L, boost::tuples::length<Tuple>::value, 0))
  12:     {
  13:         assert(lua_gettop(L) == top - m_params + 1);
  14:  
  15: NO_EXCEPTIONS
  16:         throw luabind::error(L);
  17: #else
  18:         error_callback_fun e = get_error_callback();
  19:         if (e) e(L);
  20:     
  21:         assert(0 && "the lua function threw an error and exceptions are disabled."
  22:                 " If you want to handle the error you can use luabind::set_error_callback()");
  23:         std::terminate();
  24: #endif
  25:     }
  26:     // pops the return values from the function call
  27:     stack_pop pop(L, lua_gettop(L) - top + m_params);
  28: }

11行代码中调用的是lua_resume, 返回的是运行错误, 但是被13行的assert挡住了, 无法通过第16行抛出异常被外面捕获.

因此,尝试注释第13行, 再测试, 可以在lua抛出错误后, 在栈顶捕获到coroutine函数resume时报出的错误信息.问题解决

 

对于lua的coroutine, 网上资料不多, 这里有一篇比较详细的代码

我比较疑惑的是, 有没有必要将代码在dofile或者dobuffer时, 必须传入newthread出的state? 如果还是传入原始的state会有什么影响?

欢迎各位有此经验的讨论

posted @ 2012-03-27 10:38 战魂小筑 阅读(768) | 评论 (2)编辑 收藏

仅列出标题  下一页