zoukankan      html  css  js  c++  java
  • Scut游戏服务器免费开源框架-3

    Scut游戏服务器免费开源框架--快速开发(3)

    Scut快速开发(3)

    1        开发环境

    需要安装的软件

    a)        消息队列

    b)        数据库,Sql2005以上版本

    c)        VS2010开发工具(.Net Framework 4.0以上)

    2        代码框架

    项目划分三层:实体层,组件层,业务逻辑层(脚本层);模型层主要是数据实体映射,自定义缓存结构;组件层主要负责实现中层层扩展功能;业务逻辑层主要负责实现游戏功能;

    2.1     数据库创建

    游戏划分成三个库:DemoConfig库(负责存储游戏配置数据) 、DemoData库(负责存储游戏玩家数据)和DemoLog库(游戏玩家日志记录数据);如图

                           

    这里只为每个库建立一张表:

    地图信息配置表(MapInfo)DemoConfig库

    复制代码
    create table MapInfo
    (
        MapId int not null,
        MapName varchar(50) not null,
        PointX  int,
        pointY  int,
        primary key(MapId)
    )
    复制代码

    玩家表(GameUser)DemoData库

    复制代码
    create table GameUser
    (
        UserID int not null,
        NickName varchar(50) not null,
        Pid  varchar(50),
        UserStatus int,
        Property  text,
        CreateDate  datetime,
        primary key(UserID)
    )
    go
    --继承BaseUser基类
    EXECUTE sp_addextendedproperty N'MS_Description', '玩家信息[:BaseUser]', N'user', N'dbo', N'table', N'GameUser', NULL, NULL
    Go
    --自定义枚举
    EXECUTE sp_addextendedproperty N'MS_Description', '玩家状态[Enum<UserStatus>]', N'SCHEMA', N'dbo',  N'table', N'GameUser', N'column', N'UserStatus'
    Go
    --自定义实体类
    EXECUTE sp_addextendedproperty N'MS_Description', '玩家属性[CacheList<Property>]', N'SCHEMA', N'dbo',  N'table', N'GameUser', N'column', N'Property'
    复制代码

    玩家登录日志表(UserLoginLog)

    复制代码
    create table UserLoginLog
    (
        ID int not null identity(1,1),
        UserId varchar(50) not null,
        Pid  varchar(50),
        Ip   varchar(50),
        CreateDate  datetime,
        primary key(ID)
    )
    复制代码

    2.2     数据实体生成

    使用CodeSmith工具制作实体模板(已提供现有模板)来生成我们需要的实体类;打开EntityBuilding.cst文件,弹出窗口中进行配置相应属性,配置完成后点击“Generate”生成实体类;如图:

    Config库配置,设置只读权限

     

    增加数据库连接

     

     

    Data库配置,设置成读写权限

     

    Log库配置,设置成只写权限

     

    打开F:GameDemoCode目录,可查看生成的数据实体类,创建好项目后将实体类文件Copy到项目对应的目录下

    2.3     项目搭建

    2.3.1        创建解决方案

    打开VS2010 在菜单上选择 文件 -> 新建 -> 项目;弹出“新建项目”对话窗口,在左则展开“其它项目类型”,选择“Visual Studio解决方案”,选择“.Net Framework 4.0”后,输出项目名称及位置,再点击“确定”;如图:

     

    2.3.2        创建项目

    在“资源管理器”中添加新的几个项目Model、Lang、Com、Bll、HostServer;Model项目:负责从数据库中导出表的数据实体类映射;

    Lang项目:负责多语言包;

    Com项目:负责中间层组件扩展处理,及中间层业务实体类;

    Bll项目:负责业务逻辑处理;

    HostServer项目:控制台启动程序,及脚本(Python)业务逻辑处理;

    如图:

     

     

    创建项目结果如下:

     

    设置控制台程序“HostServer”属性为“.Net Framework 4.0”

     

    设置成“Release”编译方式

     

    2.3.3   Model项目

    组件引用

    项目

    引用路径

    Model

    PluginRedisprotobuf-net.dll

    FrameworkV5.1yGames.Framework.Common.dll

    FrameworkV5.1yGames.Framework.dll

    GameV2.6yGames.Framework.Game.dll

    组件详情

     

    目录划分

    划分配置库(ConfigModel)、玩家信息库(DataModel)、玩家日志库(LogModel)和自定义类型数据实体(Model)目录存储数据实体类; Enum目录存储自定枚举;

    步骤

    接着将之前使用CodeSmith生成好的实体类复制到相应的ConfigModel、DataModel和LogModel目录下,并在VS Model项目中将其包括在项目中;

    接着编译Model项目会发现在GameUser类中缺少些命名空间引用,UserStatus枚举和Property类没有定义(注意到没,这些缺少的类和枚举就是在创建表时增加的配置);

    在Model项目中分别在Enum目录与Model目录下增加UserStatus自定义枚举和Property自定义类;接收编译就可以成功了。(建议在GameUser类的构造函数中实例化Property属性及其它引用类型属性,在业务层使用中减少判断Null语句)

    DbConfig

    在生成实体类时,生成配置ConnectKey项中使用

    复制代码
    public class DbConfig
    {
        public const string Config = "DemoConfig";
        public const string Data = "DemoData";
        public const string Log = "DemoLog";
        public const int GlobalPeriodTime = 0;
        public const int PeriodTime = 0;
        public const string PersonalName = "UserId";
    }
    //UserId:是玩家库中表的主键字段名称; 红色部分是需要修改的
    复制代码

    项目建立完成如图:

     

    2.3.4   Lang项目

    组件引用

    项目

    引用路径

    Lang

    FrameworkV5.1yGames.Framework.Common.dll

    GameV2.6yGames.Framework.Game.Lang.dll

    负责处理多语言包配置,需要实现中层提供的语言包;以下是定义类

    LanguageManager

    复制代码
    public class LanguageManager
    {
        private static object thisLock = new object();
        private static Dictionary<LangEnum, IGameLanguage> _langTable = new Dictionary<LangEnum, IGameLanguage>();
        private static LangEnum _langEnum;
     
        static LanguageManager()
        {
            _langEnum = (ConfigUtils.GetSetting("LanguageType", "0")).ToEnum<LangEnum>();
            LanguageHelper.SetLang(_langEnum);
        }
     
        public static IGameLanguage GetLang()
        {
            return GetLang(_langEnum);
        }
     
        public static IGameLanguage GetLang(LangEnum langEnum)
        {
            IGameLanguage lang = null;
            if (!_langTable.ContainsKey(langEnum))
            {
                lock (thisLock)
                {
                    if (!_langTable.ContainsKey(langEnum))
                    {
                        switch (langEnum)
                        {
                            case LangEnum.ZH_CN:
                                _langTable.Add(langEnum, new GameZhLanguage());
                                break;
                            default:
                                throw new Exception("Language is error.");
                        }
                    }
                }
            }
            lang = _langTable[langEnum];
            return lang;
        }
     
    }
    复制代码

    IGameLanguage接口

    复制代码
    public interface IGameLanguage : ILanguage
    {
        #region
        /// <summary>
        /// 君主帐号
        /// </summary>
        int SystemUserId { get; }
        /// <summary>
        /// 玩家名称
        /// </summary>
        string KingName { get; }
        string Date_Yesterday { get; }
        string Date_BeforeYesterday { get; }
        string Date_Day { get; }
        string St1002_GetRegisterPassportIDError { get; }
        string St1005_NickNameOutRange { get; }
        string St1005_NickNameExistKeyword { get; }
        string St1005_NickNameExist { get; }
        string St1006_PasswordTooLong { get;}
        string St1006_ChangePasswordError { get;}
        string St1006_PasswordError { get;}
        string St1066_PayError { get; }
     
        #endregion
    }
    复制代码

    GameZhLanguage

    复制代码
    class GameZhLanguage : BaseZHLanguage, IGameLanguage
    {
        public int SystemUserId
        {
            get { return 1000000; }
        }
     
        public string KingName
        {
            get { return "系统"; }
        }
     
        public string Date_Yesterday { get { return "昨天"; } }
        public string Date_BeforeYesterday { get { return "前天"; } }
        public string Date_Day { get { return "{0}天前"; } }
    
        public string St1002_GetRegisterPassportIDError { get { return "获取注册通行证ID失败!"; } }
     
        public string St1005_NickNameOutRange { get { return "您的昵称输入有误,请重新输入!"; } }
        public string St1005_NickNameExistKeyword { get { return "您输入的昵称存在非法字符,请重新输入!"; } }
        public string St1005_NickNameExist { get { return "您输入的昵称已存在,请重新输入!"; } }
     
        public string St1006_PasswordTooLong { get { return "输入错误,请输入4-12位数字或字母!"; } }
        public string St1006_ChangePasswordError { get { return "修改密码失败!"; } }
        public string St1006_PasswordError { get { return "密码格式错误!"; } }
        public string St1066_PayError { get { return "充值失败"; } }
    复制代码

    2.3.5   Com项目

    组件引用

    项目

    引用路径

    Com

    PluginRedisprotobuf-net.dll

    FrameworkV5.1yGames.Framework.Common.dll

    FrameworkV5.1yGames.Framework.dll

    GameV2.6yGames.Framework.Game.Lang.dll

    GameV2.6yGames.Framework.Game.dll

    Model

    Lang

    划分中间件业务实体(Model),聊天组件(Chat)与排行榜(Rank)等目录,如图:

     

    需要使用中间层的功能,请参考《中间层使用文档》

    2.3.6   Bll项目

    组件引用

    项目

    引用路径

    Bll

    PluginNewtonsoft.Json.dll

    Plugin PythonIronPython.dll

    FrameworkV5.1yGames.Framework.Common.dll

    FrameworkV5.1yGames.Framework.dll   FrameworkV5.1yGames.Framework.Plugin.dll

    FrameworkV5.1yGames.Framework.RPC.dll

    GameV2.6yGames.Framework.Game.Lang.dll

    GameV2.6yGames.Framework.Game.dll

    GameV2.6ZyGames.Framework.Game.Contract.dll

    Model

    Lang

    Com

    功能划分

    创建Action目录划分接口协议处理逻辑;主要提供中间层定义的固定协议接口,如:登录(1004)与建角(1005)及充值中间层接口

    ActionIDDefine

    复制代码
    public class ActionIDDefine
    {
        ///<summary>
        ///客户端注册Socket
        ///</summary>
        public const Int16 Cst_Action100 = 100;
     
        ///<summary>
        ///错误日志
        ///</summary>
        public const Int16 Cst_Action404 = 404;
     
        ///<summary>
        ///注册通行证ID获取接口
        ///</summary>
        public const Int16 Cst_Action1002 = 1002;
     
        ///<summary>
        ///用户注册
        ///</summary>
        public const Int16 Cst_Action1003 = 1003;
     
        ///<summary>
        ///用户登录
        ///</summary>
        public const Int16 Cst_Action1004 = 1004;
     
        ///<summary>
        ///创建角色
        ///</summary>
        public const Int16 Cst_Action1005 = 1005;
    }
    复制代码

     BaseAction

    复制代码
    public abstract class BaseAction : AuthorizeAction
    {
        protected BaseAction(short actionID, HttpGet httpGet)
            : base(actionID, httpGet)
        {
        }
     
        protected override bool IgnoreActionId
        {
            get
            {
                //排除不需要登录授权的协议接口
                return actionId == ActionIDDefine.Cst_Action404;
            }
        }
    }
    复制代码

    2.3.7   HostServer项目

    组件引用

    项目

    引用路径

    HostServer

    PluginNewtonsoft.Json.dll

    PluginNLog.dll

    PluginRedisprotobuf-net.dll

     

    Plugin PythonIronPython.dll

    Plugin PythonIronPython.Modules.dll

    Plugin PythonMicrosoft.Dynamic.dll

    Plugin PythonMicrosoft.Scripting.dll

     

    PluginRedisServiceStack.dll

    PluginRedis ServiceStack.Common.dll

    PluginRedis ServiceStack.Interfaces.dll  

    PluginRedis ServiceStack.Redis.dll

    PluginRedis ServiceStack.Text.dll

     

    FrameworkV5.1yGames.Framework.Common.dll

    FrameworkV5.1yGames.Framework.dll   FrameworkV5.1yGames.Framework.Plugin.dll

    FrameworkV5.1yGames.Framework.RPC.dll

    GameV2.6yGames.Framework.Game.Lang.dll

    GameV2.6yGames.Framework.Game.dll

    GameV2.6ZyGames.Framework.Game.Contract.dll

    Model

    Lang

    Com

    Bll

    功能划分

    划分PyScript目录,存放Python脚本文件;层次如图:

     

    Action目录:处理请求与响应的脚本,可以协议生成器工具中Copy部分模板;

    Lib目录:Python中间层脚本,复制Scut开发包中的PythonLib目录;

    Remote:应用程序之间内通讯,访问时有IP访问限制;

    Route.config.xml:是请求路由配置表,格式如下:

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <config>
      <!--Python安装类库路径-->
      <lib path="D:PythonLib" />
      <route-list>
        <!--配置Action路由
          action:映射的Action代码
          path:指定执行的脚本路径
          ignoreAuthorize:是否不需要登录授权,true:不需要登录授权
        -->
        <route action="404" path="Actionaction404.py"/>
        <route action="1009" path="Actionaction1009.py"/>
      </route-list>
    </config>
    复制代码

     GameHostApp

    复制代码
    public class GameHostApp : GameHost
    {
        //public override void Listen()
        //{
            //这里处理请求与响应模式,设置连接超时等些参数
            //ServiceProxy.Listen(IpAddress, Port, conn, recv, inacv, connectCount);
            //ListenAfter();
        //}
     
        protected override void DoBindAfter()
        {
            //这里处理程序启动时逻辑
        }
     
        protected override void OnRequested(HttpGet httpGet, IGameResponse response)
        {
            //这里处理请求与响应模式
        }
     
        protected override void OnCallRemote(string route, HttpGet httpGet, MessageHead head, MessageStructure structure)
        {
            //这里处理服务器应用程序间通信
        }
     
        protected override void OnClosed(ChannelContext context, string remoteaddress)
        {
            //这里处理服务被关闭逻辑
        }
     
        protected override void OnSocketClosed(ChannelContext context, string remoteaddress)
        {
            //这里处理Socket服务被关闭逻辑
        }
     
        protected override void OnServiceStop(object sender, EventArgs eventArgs)
        {
            //这里处理服务被停止逻辑
        }
    }
    复制代码

    Program

    复制代码
    class Program
    {
        static void Main(string[] args)
        {
            GameHost.Start();
        }
    }
    复制代码

    宿主程序Config配置

    复制代码
    <?xml version="1.0"?>
    <configuration>
     
      <configSections>
        <section name="zyGameBaseBll" type="ZyGames.Framework.Game.Configuration.ZyGameBaseBllSection,ZyGames.Framework.Game"/>
        <section name="zyGameBase-GM" type="ZyGames.Framework.Game.Command.GmSection,ZyGames.Framework.Game"/>
        <section name="sdkChannel" type="ZyGames.Framework.Game.Sns.Section.SdkChannelSection,ZyGames.Framework.Game"/>
      </configSections>
      <appSettings>
        <!--消息队列异步写库配置-->
        <add key="MessageQueuePath" value=".private$DemoCmdSql"></add>
        <add key="MessageQueueNum" value="10"></add>
       
        <!--宿主程序启动配置-->
        <add key="Game.Host.TypeName" value="Demo.HostServer.GameHostApp,Demo.HostServer"/>
        <add key="Game.Action.TypeName" value="Demo.Bll.Action.Action{0},Demo.Bll"/>
        <!--Port:接收请求的端口
          connectTimeout:建立连接和传送数据的超时间(默认10秒)
          receiveTimeout:保持连接的情况下,空闲超时触发Closing事件,尽量最大,默认10分钟
          inactivityTimeout:连接断开的情况下,空闲超时触发Faulted事件,要小于receiveTimeout时间间隔(默认5秒)
          connectCount:允许连接的并发数,默认100
        -->
        <add key="Game.Port" value="9001" />
        <add key="Game.wcf.ConnectTimeout" value="10" />
        <add key="Game.wcf.ReceiveTimeout" value="1800" />
        <add key="Game.wcf.InactivityTimeout" value="5" />
        <add key="Game.wcf.ConnectCount" value="1000"/>
     
        <!--Code:游戏代码;ServerId:游戏分区代码-->
        <add key="Product.Code" value="1"/>
        <add key="Product.Name" value="游戏名称"/>
        <add key="Product.ServerId" value="1"/>
        <!-- 配置语言版本,ZH_CN:简体,EN_US:英文,BIG5_TW:繁体 -->
        <add key="LanguageType" value="ZH_CN"/>
        <!--发布的版本类型,Debug:测试,Release:正式-->
        <add key="PublishType" value="Debug"></add>
        <!--GM功能是否开启,true:开启-->
        <add key="EnableGM" value="true"></add>
        <!--协议接口请求超时监控,单位毫秒-->
        <add key="ActionTimeOut" value="600"></add>
        <!--Python脚本配置
          Python_IsDebug:是否开启调试功能
          PythonRootPath:脚本路径,相对于程序运行目录
        -->
        <add key="Python_IsDebug" value="true"/>
        <add key="PythonRootPath" value="....PyScript"/>
       
        <!--游戏缓存配置
          global.period:全局缓存生命周期24小时
          user.period:玩家缓存生命周期8小时
          CacheManager_Interval:缓存监控时间间隔
        -->
        <add key="Cache.global.period" value="86400"/>
        <add key="Cache.user.period" value="28800"/>   
        <add key="CacheManager_Interval" value="6000"/>
       
        <!--通用组件配置开始-->
        <add key="PayDB_ConnectionString" value="Data Source={0};Database=PayDB;{1}; Pooling=true;"/>
        <add key="PayDB_Server" value="."/>
        <add key="PayDB_Acount" value="加密密码"/>
        <add key="Snscenter_ConnectionString" value="Data Source={0};Database=snscenter;{1}; Pooling=true;"/>
        <add key="Snscenter_Server" value="."/>
        <add key="Snscenter_Acount" value="加密密码"/>
        <!--通用组件配置结束-->
      </appSettings>
      <connectionStrings>
        <add name="DemoConfig" providerName="" connectionString="Data Source=.;Database=DemoConfig;Uid=;Pwd=; Pooling=true;"/>
        <add name="DemoData" providerName="" connectionString="Data Source=.;Database=DemoData;Uid=;Pwd=; Pooling=true;"/>
        <add name="DemoLog" providerName="" connectionString="Data Source=.;Database=DemoLog;Uid=;Pwd=; Pooling=true;"/>
      </connectionStrings>
     
      <!--业务层自定义配置-->
      <zyGameBaseBll>
        <login defaultType="ZyGames.Framework.Game.Sns.Login36you,ZyGames.Framework.Game">
          <retailList>
            <add id="0000" type="ZyGames.Framework.Game.Sns.Login36you,ZyGames.Framework.Game" args="Pid,Pwd,DeviceID"/>
          </retailList>
        </login>
      </zyGameBaseBll>
     
      <zyGameBase-GM>
        <command>
          <!--GM:cache-->
        </command>
      </zyGameBase-GM>
     
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
      </startup>
    </configuration>
    复制代码

    2.4     定义协议

    打开协议生成器工具,增加一个“Demo”项目方案,接着在增加协议(或从现有项目中Copy相同的接口协议),接着定义客户端提供的请求参数和服务器下发的参数(支持多行的格式,字段类型:Record与End组合);定义好客户端与服务器之前通讯的接口后,使用自动生成的服务端Python代码(客户端使用Lua脚本代码)复制到已创建的Python接口文件,如图:

     

    2.5     项目部署

    需要架设Socket方式的分发器(或Http方式的分发器,两都可一起架设),将客户端请求先通过Socket或Http分发器再转发到游戏服务器(HostServer程序)上;

    2.6     Socket分发器

    使用OA部署功能架设分发器;在本机使用批处理文件,如下:

    复制代码
    C:WindowsSystem32schtasks.exe /Create /RU system /TN DemoSocketApp /SC ONSTART /TR " F:Demo SocketDoudizhuyGames.Framework.Game.SocketServer.exe" /F
    复制代码

    2.7     HostServer程序

    使用OA部署功能架设游戏服;在本机使用批处理文件,加在任务计划中启动,如下:

    复制代码
    C:WindowsSystem32schtasks.exe /Create /RU system /TN DemoHostApp /SC ONSTART /TR "F:DemoDemo.HostServer.exe" /F
    复制代码

      

    开源地址
    GitHub地址https://github.com/ScutGame

     
     

    呵呵第一次认真写一次技术问题 今天遇到的Linq未提交之前插入/修改时重新查询不准确问题

     

    来园子已经两年了,每次都是看,这次咱也写一次。

    说一下今天遇到的Linq问题:

    每一次插入流水表时,都需要查找表中最大的流水号+1,并且将该流水号返回,但是在同一个SubmitChange之内插入多条时,流水号就一直是表中实际最大的,而不是我上一次插入的最大的。不描述了 贴代码:

    这个是DataContext

    复制代码
     View Code
    复制代码

     两个公用方法:

    复制代码
     View Code
    复制代码

    表结构:

    复制代码
     View Code
    复制代码

    第一种情况:修改提交前可以重新查询获得已经更新的内容,读取的是内存中的 未使用隐式事务

    复制代码
     View Code
    复制代码
    第二种情况:添加后未提交前取得的最大的Number值永远是数据表中真实的值,不是内存中的 未使用隐式事务
    复制代码
     View Code
    复制代码

    第三种情况:开启隐式事务,添加后提交前取得的最大的Number值是我想要的值,最后再Commit

    复制代码
     View Code
    复制代码

    这就是今天遇到的问题,现在没办法只能开启隐式事务了,不过为什么第二种情况达不到我想要的结果呢?求大神解。

     
     
     
    分类: .Net
     
  • 相关阅读:
    CentOS7.6下安装qt-creator
    chromedriver下载地址
    selenium 基于Autolt工具对于Windows窗口上传文件操作
    Selenium+java
    jmeter删除历史打开的脚本记录
    echarts 各种特效图
    SpringBoot启动-问题解决:Could not locate executable nullinwinutils.exe in the Hadoop binaries
    pinyin4j
    JMeter内存溢出:java.lang.OutOfMemoryError: Java heap space解决方法
    selenium自动化测试中升级chrome78版本带来的问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3273594.html
Copyright © 2011-2022 走看看