Legend of Mir(传奇)官方源码学习3服务端程序初始化过程

Posted シ゛甜虾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Legend of Mir(传奇)官方源码学习3服务端程序初始化过程相关的知识,希望对你有一定的参考价值。

一、窗体事件

整个服务端界面只有2个事件,一个SMain_Load一个SMain_FormClosing,一个负责初始化,一个负责程序关闭保存

SMain_Load事件

private void SMain_Load(object sender, EventArgs e)

    EditEnvir.LoadDB();
    Envir.Start();
    AutoResize();

这里要讲一下比较有趣的变量(用到了=> 运算符(C# 参考))

public static Envir Envir => Envir.Main;
public static Envir EditEnvir => Envir.Edit;

有的人可能看不懂了,lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者 “成为”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体,这里的实际效果与下面代码效果相同。

public static Envir Envir = Envir.Main;
public static Envir EditEnvir = Envir.Edit;

一个Envir.Main,和一个Envir.Edit,创建的竟然是同一个类,看来这个类里面有不少公共的东西在里面,要不然也不会放到一起不拆分开来。

public static Envir Main  get;  = new Envir();
public static Envir Edit  get;  = new Envir();

一个负责加载游戏资源,一个负责执行游戏逻辑,可能是方便多人开发的这么写的具体为啥这么写不知道,总感觉怪怪的,继续往下看,可能就知道了

EditEnvir.LoadDB(); //也就是Envir Edit
Envir.Start();      //也就是Envir Main

整个代码还是比较清晰的,Envir类一共2个锁

public static object AccountLock = new object();    //账户锁
public static object LoadLock = new object();       //Load锁

 3个路径配置

public static readonly string DatabasePath = Path.Combine(".", "Server.MirDB");     //主数据库路径
public static readonly string AccountPath = Path.Combine(".", "Server.MirADB");     //账户数据库路径
public static readonly string BackUpPath = Path.Combine(".", "Back Up");            //客户端升级文件列表?

LoadDB(),加载地图NPC等等数据,如果文件不存在就创建DB文件,并把默认设置写进去

//Server DB
public int MapIndex, ItemIndex, MonsterIndex, NPCIndex, QuestIndex, GameshopIndex, ConquestIndex, RespawnIndex;
public List<MapInfo> MapInfoList = new List<MapInfo>();
public List<ItemInfo> ItemInfoList = new List<ItemInfo>();
public List<MonsterInfo> MonsterInfoList = new List<MonsterInfo>();
public List<MagicInfo> MagicInfoList = new List<MagicInfo>();
public List<NPCInfo> NPCInfoList = new List<NPCInfo>();
public DragonInfo DragonInfo = new DragonInfo();
public List<QuestInfo> QuestInfoList = new List<QuestInfo>();
public List<GameShopItem> GameShopList = new List<GameShopItem>();
public List<RecipeInfo> RecipeInfoList = new List<RecipeInfo>();
public Dictionary<int, int> GameshopLog = new Dictionary<int, int>();

这个DB文件呢,就很简单,单纯的二进制存储

 简单有直接写DB如下

public void SaveDB()

    using (var stream = File.Create(DatabasePath))
    using (var writer = new BinaryWriter(stream))
    
        writer.Write(Version);
        writer.Write(CustomVersion);
        writer.Write(MapIndex);
        writer.Write(ItemIndex);
        writer.Write(MonsterIndex);
        writer.Write(NPCIndex);
        writer.Write(QuestIndex);
        writer.Write(GameshopIndex);
        writer.Write(ConquestIndex);
        writer.Write(RespawnIndex);

        writer.Write(MapInfoList.Count);
        for (var i = 0; i < MapInfoList.Count; i++)
            MapInfoList[i].Save(writer);

        writer.Write(ItemInfoList.Count);
        for (var i = 0; i < ItemInfoList.Count; i++)
            ItemInfoList[i].Save(writer);

        writer.Write(MonsterInfoList.Count);
        for (var i = 0; i < MonsterInfoList.Count; i++)
            MonsterInfoList[i].Save(writer);

        writer.Write(NPCInfoList.Count);
        for (var i = 0; i < NPCInfoList.Count; i++)
            NPCInfoList[i].Save(writer);

        writer.Write(QuestInfoList.Count);
        for (var i = 0; i < QuestInfoList.Count; i++)
            QuestInfoList[i].Save(writer);

        DragonInfo.Save(writer);
        writer.Write(MagicInfoList.Count);
        for (var i = 0; i < MagicInfoList.Count; i++)
            MagicInfoList[i].Save(writer);

        writer.Write(GameShopList.Count);
        for (var i = 0; i < GameShopList.Count; i++)
            GameShopList[i].Save(writer);

        writer.Write(ConquestInfos.Count);
        for (var i = 0; i < ConquestInfos.Count; i++)
            ConquestInfos[i].Save(writer);

        RespawnTick.Save(writer);
    

Envir.Start();  加载完数据,就准备开始游戏,这里再次加载DB数据,这么看前期加载只是个测试

public void Start()

    if (Running || _thread != null) return;

    Running = true;

    _thread = new Thread(WorkLoop) IsBackground = true;
    _thread.Start();

这里启动了一个新的线程,开始加载地图和NPC等资源

StartEnvir();                          //加载地图和NPC等资源
var canstartserver = CanStartEnvir();  //效验BOOS怪物是否加载

效验BOOS怪物是否加载,如果BOOS没有顺利加载停止服务器运行

这个代码就比较有意思,多少个线程就弄多少个链表

if (Settings.Multithreaded)

    for (var j = 0; j < MobThreads.Length; j++)
    
        MobThreads[j] = new MobThread();
        MobThreads[j].Id = j;
    

每个链表传递给一个工作线程,如果Settings.Multithreaded==0,那么就只有主线程WorkLoop在工作,看样子,MobThread里面有的Envir里面也有,果不其然

if (Settings.Multithreaded)

    for (var j = 0; j < MobThreads.Length; j++)
    
        var Info = MobThreads[j];
        if (j <= 0) continue;
        MobThreading[j] = new Thread(() => ThreadLoop(Info)) IsBackground = true;
        MobThreading[j].Start();
    

ThreadLoop代码 

private void ThreadLoop(MobThread Info)

    Info.Stop = false;
    var starttime = Time;
    try
    
        var stopping = false;
        if (Info._current == null)
            Info._current = Info.ObjectsList.First;
        stopping = Info._current == null;
        //while (stopping == false)
        while (Running)
        
            if (Info._current == null)
                Info._current = Info.ObjectsList.First;
            else
            
                var next = Info._current.Next;

                //if we reach the end of our list > go back to the top (since we are running threaded, we dont want the system to sit there for xxms doing nothing)
                if (Info._current == Info.ObjectsList.Last)
                
                    next = Info.ObjectsList.First;
                    Info.LastRunTime = (Info.LastRunTime + (Time - Info.StartTime)) / 2;
                    //Info.LastRunTime = (Time - Info.StartTime) /*> 0 ? (Time - Info.StartTime) : Info.LastRunTime */;
                    Info.StartTime = Time;
                
                if (Time > Info._current.Value.OperateTime)
                
                    if (Info._current.Value.Master == null)//since we are running multithreaded, dont allow pets to be processed (unless you constantly move pets into their map appropriate thead)
                    
                        Info._current.Value.Process();
                        Info._current.Value.SetOperateTime();
                    
                
                Info._current = next;
            
            //if it's the main thread > make it loop till the subthreads are done, else make it stop after 'endtime'
            if (Info.Id == 0)
            
                stopping = true;
                for (var x = 1; x < MobThreads.Length; x++)
                    if (MobThreads[x].Stop == false)
                        stopping = false;
                if (!stopping) continue;
                Info.Stop = stopping;
                return;
            

            if (Stopwatch.ElapsedMilliseconds <= Info.EndTime || !Running) continue;
            Info.Stop = true;
            lock (_locker)
            
                while (Info.Stop) Monitor.Wait(_locker);
            
        
    
    catch (Exception ex)
    
        if (ex is ThreadInterruptedException) return;
        MessageQueue.Enqueue(ex);

        File.AppendAllText(Path.Combine(Settings.ErrorPath, "Error.txt"),
            $"[Now] exEnvironment.NewLine");
    
    //Info.Stop = true;

 开启网络,HTTPService默认是不开启的

StartNetwork();     //开启网络,加载
if (Settings.StartHTTPService)

    http = new HttpServer();
    http.Start();
     

从这里开始,就可以登录了,下面就是一些游戏逻辑

玩家逻辑,里面有走,技能,交易等,下一篇文章详细讲解

AdjustLights();//时间切换,黑天,白天等

lock (Connections)

    for (var i = Connections.Count - 1; i >= 0; i--)
    
        //玩家执行一步
        Connections[i].Process();
    

其他

//地图执行一步
for (var i = 0; i < MapList.Count; i++)

    MapList[i].Process();


DragonSystem?.Process();

Process();//执行一步
                        
if (Time >= saveTime)

    //数据保存,默认5分钟存储一次
    saveTime = Time + Settings.SaveDelay * Settings.Minute;
    BeginSaveAccounts();
    SaveGuilds();
    SaveGoods();
    SaveConquests();


if (Time >= userTime)

    //5分钟给所有玩家发送一下在线玩家数量
    userTime = Time + Settings.Minute * 5;
    Broadcast(new S.Chat
        
            Message = string.Format(GameLanguage.OnlinePlayers, Players.Count),
            Type = ChatType.Hint
        );

以上是关于Legend of Mir(传奇)官方源码学习3服务端程序初始化过程的主要内容,如果未能解决你的问题,请参考以下文章

Legend of Mir(传奇)官方源码学习2运行试玩及GM命令

想学开服技术,先了解服务端的组成

传奇版本QM QF是啥

传奇添加地图与配置参数详解

传奇私服地图设置计时扣元宝

传奇怎么设置沙巴克自动攻城