FSD源码探索之Whazzup.txt

最近想要基于fsd来开发一套飞控系统,那么对FSD源码的研究是十分重要的。这篇文章将记录FSD与外界进行明文数据沟通的最重要的一环——Whazzup.txt

源码

fsd是基于c++构建的,我在这里将whazzup的生成代码贴在这里,其位置位于fsd.cpp中

// WhazzUp Start
    if ((now-prevwhazzup)>=WHAZZUPCHECK)
    {
        configentry *entry;
        configgroup *sysgroup=configman->getgroup("system");
        if (sysgroup)
            if ((entry=sysgroup->getentry("whazzup"))!=NULL)
            {
                if (whazzupfile)
                    free(whazzupfile);
                whazzupfile=strdup(entry->getdata());
                char whazzuptemp[100];
                sprintf(whazzuptemp,"%s%s", whazzupfile, ".tmp");
                prevwhazzup=now;
                if (fileopen==0)
                {
                    FILE *wzfile=fopen(whazzuptemp, "w");
                    if (wzfile)
                    {
                        //Ready to write data
                        fileopen = 1;
                        char s[32];
                        fprintf(wzfile,"%s%s\n","![DateStamp]",sprintgmtdate(now,s));
                        fprintf(wzfile,"%s\n","!GENERAL");
                        fprintf(wzfile,"%s = %d\n", "VERSION", 4);
                        fprintf(wzfile,"%s = %d\n", "RELOAD", 5);
                        fprintf(wzfile,"%s = %s\n", "UPDATE", sprintgmt(now, s));
                        client *tempclient;
                        flightplan *tempflightplan;
                        server *tempserver;
                        int clients=0;
                        for (tempclient=rootclient; tempclient; tempclient=tempclient->next)
                            clients++;
                        fprintf(wzfile,"%s = %d\n", "CONNECTED CLIENTS", clients);
                        int servers=0;
                        for (tempserver=rootserver; tempserver; tempserver=tempserver->next)
                            servers++;
                        fprintf(wzfile,"%s = %d\n", "CONNECTED SERVERS", servers);
                        fprintf(wzfile,"%s\n","!CLIENTS");
                        char dataseg1[150];
                        char dataseg2[150];
                        char dataseg3[150];
                        char dataseg4[150];
                        char dataseg5[150];
                        char dataseg6[2000];
                        char dataseg7[50];
                        for (tempclient=rootclient; tempclient; tempclient=tempclient->next)
                        {
                            sprintf(dataseg1,"%s:%s:%s:%s", tempclient->callsign, tempclient->cid, tempclient->realname, tempclient->type==CLIENT_ATC?"ATC":"PILOT");
                            if (tempclient->frequency!=0 && tempclient->frequency<100000 && tempclient)
                                sprintf(dataseg2,"1%02d.%03d", tempclient->frequency/1000, tempclient->frequency%1000);
                            else
                                sprintf(dataseg2,"%s","");
                            tempflightplan=tempclient->plan;
                            if (tempclient->lat!=0 && tempclient->altitude < 100000 && tempclient->lon != 0)
                                sprintf(dataseg3,"%f:%f:%d:%d", tempclient->lat, tempclient->lon, tempclient->altitude, tempclient->groundspeed);
                            else
                                sprintf(dataseg3,"%s",":::");
                            if (tempflightplan)
                                sprintf(dataseg4,"%s:%d:%s:%s:%s", tempflightplan->aircraft, tempflightplan->tascruise, tempflightplan->depairport, tempflightplan->alt, tempflightplan->destairport);
                            else
                                sprintf(dataseg4,"%s","::::");
                            sprintf(dataseg5,"%s:%s:%d:%d:%d:%d", tempclient->location->ident, tempclient->protocol, tempclient->rating, tempclient->transponder, tempclient->facilitytype, tempclient->visualrange);
                            if (tempflightplan)
                                sprintf(dataseg6,"%d:%c:%d:%d:%d:%d:%d:%d:%s:%s:%s", tempflightplan->revision, tempflightplan->type, tempflightplan->deptime, tempflightplan->actdeptime, tempflightplan->hrsenroute, tempflightplan->minenroute, tempflightplan->hrsfuel, tempflightplan->minfuel, tempflightplan->altairport, tempflightplan->remarks, tempflightplan->route);
                            else
                                sprintf(dataseg6,"%s","::::::::::");
                            sprintf(dataseg7,"::::::%s:%d", sprintgmt(tempclient->starttime,s), tempclient->pbh);
                            fprintf(wzfile,"%s:%s:%s:%s:%s:%s:%s\n", dataseg1, dataseg2, dataseg3, dataseg4, dataseg5, dataseg6, dataseg7);
                        }
                        char dataline[150];
                        fprintf(wzfile,"%s\n","!SERVERS");
                        for (tempserver=rootserver; tempserver; tempserver=tempserver->next)
                            if (strcmp(tempserver->hostname,"n/a") != 0)
                            {
                                sprintf(dataline,"%s:%s:%s:%s:%d", tempserver->ident, tempserver->hostname, tempserver->location, tempserver->name, tempserver->flags&SERVER_SILENT?0:1);
                                fprintf(wzfile,"%s\n",dataline);
                            };
                        fclose(wzfile);
                        remove(whazzupfile);
                        rename(whazzuptemp, whazzupfile);
                        fileopen=0;
                    }
                    else
                        fileopen=0;
                }
            }
    }
// WhazzUp End

分析

整个whazzup分为三个部分 我将其称为基础信息、用户信息、服务器信息

基础信息

基础信息我们其实仅需要关注UPDATE , CONNECTED CLIENTS ,CONNECTED SERVERS 这几个标签即可。UPDATE显然记录的是更新的时间戳 ,CONNECTED CLIENTS记录的是连接到几个用户,CONNECTED SERVERS记录着连接到几台服务器(fsd原生支持多服务器联动)

用户信息

个人认为用户信息是最值得探究也是最为复杂的。在其源码中,我们可以看到使用了多次判断来决定是否要显示哪些信息,每个属性都是用“:”来分隔的。

首先前四个属性分别为 呼号:cid:真实姓名:类型 这些是所有用户都有的基础信息,在ECHO,Euroscpe中也是必填项,我们可以通过cid来追踪用户。类型则表示是飞行员还是管制。

接下来是 频率 这个是ATC专属的,它可以在euroscope中设置

后面两个是经纬度信息,飞行员即表示着其所在位置,ATC则是表示管制圈的中心点(存疑),这个是写死在扇区文件之中的,可以根据后面的半径来确定管制圈,这在连飞地图上非常有用。

接下来是高度:地速 这两个是在飞行员中有的 而在ATC上则是被填写了0

接下来是机型:巡航真空速:出发机场:巡航高度:目的地机场 这些是由飞行员填写的飞行计划中包含的

之后是标识:协议:等级:应答机 标识即在fsd.conf中定义的服务器标识,协议暂不清楚,等级则就是连接到服务器的用户等级1-12,可在adminhelp中查询到,应答机则就是飞行员的应答机号,ATC则标为0

然后是设施类型:视距:修订版本 这个应该是与metar有关

接下来是飞行类型:出发时间:实际出发时间:巡航飞行小时:巡航飞行分钟:燃油续航小时:燃油续航分钟。这些都是可选项,在ECHO等飞行连接器中可以填写 飞行类型则是I表示IFR,V表示VFR。

接下来是备降机场:备注:航路:起始时间。这些都是在ECHO中发送的飞行计划中的内容,起始时间之登录时间。

接下来我认为最为重要的就是PBH,"Pitch, Bank, Heading",即俯仰角、滚转角和航向角。这个数值可以通过位运算来表示飞机的姿态。根据源码中的说明:俯仰角=(pbh&4290772992)>>22,滚转角=(pbh&4190208)>>12,航向角=(pbh&4092)>>2,这样我们就可以获取飞机的姿态信息了。而在ATC中,他则表示的是在p3d,fsx中的塔台视角。

服务器信息

服务器信息比较简单 标识:服务器网络位置:服务器地区:服务器名称:服务器序号

这些都是在fsd.conf中定义的

总结

通过对whazzup的研究,我们可以通过这个文件来捕获基本所有的通信信息,对开发帮助非常之大。

呼号:CID:真实姓名:类型:频率:纬度:经度:海拔高度:地速:机型:巡航真空速:出发机场:巡航高度:目的地机场:标识:协议:等级:应答机:设施类型:视距:修订版本:飞行类型:预计起飞时间:实际起飞时间:巡航飞行小时:巡航飞行分钟:燃油续航小时:燃油续航分钟:备降机场:备注:航路:::::::登陆时间戳:偏航角

Comment