一个用于模拟飞行联飞的FSD, 使用Go语言编写
FSD支持计划同步, 计划锁定, 网页计划提交
本FSD服务器支持开箱即用, 您可以通过连续运行两次服务器可执行文件来快速搭建一个FSD服务器
第一次运行会报错并自动退出,这是正常现象,第一次运行服务器会生成配置文件模板放置到同目录
此时您可以
1.先按照配置文件简介里面的介绍完成服务器配置后再启动
2.直接运行服务端可执行文件, 服务器会使用默认配置运行
注意:默认配置下使用sqlite数据库存储数据, 而sqlite在多线程写入的时候有很严重的性能瓶颈, 且sqlite以单文件形式存储与磁盘, 受硬盘性能影响较大
感谢3370的简易测试结果:
在本地部署不考虑带宽的情况下, sqlite最多可以支持的客户端数量大约在200-300并且有概率断线
而在使用mysql数据库的时候, 可以轻松跑到400+的客户端, 还有余力(因为测试程序是用python写的, 所以测试程序产生了瓶颈)
所以我们建议
1.不要使用sqlite作为长期数据库使用
2.不要在使用sqlite作为数据库的时候进行大流量或者大压力测试
如果真的没有部署大型关系型数据库(比如mysql)的条件
那么我们建议将simulator_server开关打开, 即将本服务器作为模拟机服务器使用
因为模拟机服务器模式下, 飞行机组的飞行计划不会写入数据库, 很大一部分是缓存在内存中, 可以一定程度上解决sqlite写入性能差的问题
此项目不是纯粹的FSD项目, 同时也集成了HTTP API服务器与gRPC服务器
在此处查看更详细的说明和查看API文档
- 克隆本仓库
- 确保安装了go编译器并且版本>=1.23.4
- 在项目根目录运行如下命令
go build -x .\cmd\fsd-server\
- [可选]使用upx压缩可执行文件
i. (windows)upx.exe -9 .\fsd-server.exe
ii. (linux)upx -9 .\fsd-server
- 等待编译完成后
i. 对于Windows用户, 运行生成的fsd-server.exe
ii. 对于linux用户, 运行生成的fsd-server文件 - 首次运行会在可执行文件同目录创建配置文件
config.json
, 请在编辑配置文件后再次启动 - Enjoy
{
// 调试模式, 会输出大量日志, 请不要在生产环境中打开
"debug_mode": false,
// 配置文件版本, 通常情况下与软件版本一致
"config_version": "0.5.0",
// 服务配置
"server": {
// 通用配置项
"general": {
// 是否为模拟机服务器
// 由于需要实现检查网页提交计划于实际连线计划是否一致
// 所以飞行计划存储是用用户cid进行标识的
// 但模拟机所有的模拟机都是一个用户cid, 此时就会出问题
// 即模拟机计划错误或者无法获取到计划
// 这个时候将这个变量设置为true
// 这样服务器就会使用呼号作为标识
// 但是与此同时就失去了呼号匹配检查的功能
// 但网页提交计划仍然可用, 只是没有检查功能
// 所以将这个开关命名为模拟机服务器开关
"simulator_server": false,
// 密码加密轮数
"bcrypt_cost": 12
},
// FSD服务器配置
"fsd_server": {
// FSD名称, 会被发送到连接到服务器的客户端作为motd消息
"fsd_name": "Simple-Fsd",
// FSD服务器监听地址
"host": "0.0.0.0",
// FSD服务器监听端口
"port": 6809,
// 机场数据路径, 若不存在会自动从github下载
"airport_data_file": "data/airport.json",
// 服务器飞行路径记录间隔
// 这里间隔的意思是:当客户端每发过来N个包就记录一次位置
"pos_update_points": 1,
// FSD服务器心跳间隔
"heartbeat_interval": "60s",
// FSD服务器会话过期时间
// 在过期时间内重连, 服务器会自动匹配断开时的session
// 反之则会创建新session
"session_clean_time": "40s",
// 最大工作线程数, 也可以理解为最大同时连接的sockets数目
"max_workers": 128,
// 最大广播线程数, 用于广播消息的最大线程数
"max_broadcast_workers": 128,
// 要发送到客户端的motd消息
"motd": [
"This is my test fsd server"
]
},
// Http服务器配置
"http_server": {
// 是否启用Http服务器
"enabled": false,
// Http服务器监听地址
"host": "0.0.0.0",
// Http服务器监听端口
"port": 6810,
// Http服务器最大工作线程
"max_workers": 128,
// whazzup更新时间
"whazzup_cache_time": "15s",
// whazzup访问地址
// 需要填写外部访问地址用于little nav map的在线航班显示
// 如果你不需要little nav map的在线航班显示, 你可以不管这个
"whazzup_url_header": "http://127.0.0.1:6810",
// 代理类型
// 0 直连无代理服务器
// 1 代理服务器使用 X-Forwarded-For Http头部
// 2 代理服务器使用 X-Real-Ip Http头部
"proxy_type": 0,
// POST请求的请求体大小限制
// 将本选项设置为空字符串可以禁用大小限制
"body_limit": "10MB",
// 服务器存储配置
"store": {
// 存储类型, 可选类型为
// 0 本地存储
// 1 阿里云OSS存储
// 2 腾讯云COS存储
"store_type": 0,
// 储存桶地域, 本地存储此字段无效
"region": "",
// 存储桶名称, 本地存储此字段无效
"bucket": "",
// 访问Id, 本地存储此字段无效
"access_id": "",
// 访问秘钥, 本地存储此字段无效
"access_key": "",
// CDN访问加速域名, 本地存储此字段无效
"cdn_domain": "",
// 使用内网地址上传文件, 仅阿里云OSS存储此字段无效
"use_internal_url": false,
// 本地文件保存路径
"local_store_path": "uploads",
// 远程文件保存路径, 本地存储此字段无效
"remote_store_path": "fsd",
// 文件限制
"file_limit": {
// 图片文件限制
"image_limit": {
// 允许的最大文件大小, 单位是B
"max_file_size": 5242880,
// 允许的文件后缀名
"allowed_file_ext": [
".jpg",
".png",
".bmp",
".jpeg"
],
// 存储路径前缀
"store_prefix": "images",
// 是否在本地也保存一份
"store_in_server": false
}
}
},
"limits": {
// Api访问限速
// 每个IP的每个接口均单独计算
"rate_limit": 60,
// Api访问限速窗口
// 即 rate_limit 每 rate_limit_window
// 滑动窗口计算
"rate_limit_window": "1m",
// 用户名最小长度
"username_length_min": 4,
// 用户名最大长度(系统支持的最大长度是64)
"username_length_max": 16,
// 邮箱最小长度
"email_length_min": 4,
// 邮箱最大长度(系统支持的最大长度是128)
"email_length_max": 64,
// 密码最小长度
"password_length_min": 6,
// 密码最大长度(系统支持的最大长度是128)
"password_length_max": 64,
// 最小CID
"cid_min": 1,
// 最大CID(系统支持的最大CID为2147483647)
"cid_max": 9999,
},
// 邮箱配置
"email": {
// SMTP服务器地址
"host": "smtp.example.com",
// SMTP服务器端口
"port": 465,
// 发信账号
"username": "[email protected]",
// 发信账号密码或者访问Token
"password": "123456",
// 邮箱验证码过期时间
"verify_expired_time": "5m",
// 验证码重复发送间隔
"send_interval": "1m",
// 邮件模板定义
"template": {
// 验证码模板文件路径, 不存在会自动从Github上下载
"email_verify_template_file": "template/email_verify.template",
// 管制权限变更通知模板文件路径, 不存在会自动从Github上下载
"atc_rating_change_template_file": "template/atc_rating_change.template",
// 启用管制权限变更通知
"enable_rating_change_email": true,
// 飞控权限变更通知模板文件路径, 不存在会自动从Github上下载
"permission_change_template_file": "template/permission_change.template",
// 启用飞控权限变更通知
"enable_permission_change_email": true,
// 踢出服务器通知模板文件路径, 不存在会自动从Github上下载
"kicked_from_server_template_file": "template/kicked_from_server.template",
// 启用踢出服务器通知
"enable_kicked_from_server_email": true
}
},
// JWT配置
"jwt": {
// JWT对称加密秘钥
// 请一定要保护好这个秘钥
// 并确保不被任何不信任的人知道
// 如果该秘钥泄露, 任何人都可以伪造管理员用户
// 更安全的做法是将本字段置空, 这样每次服务器重启都会使得之前的秘钥全部失效
"secret": "123456",
// JWT主密钥过期时间
// 建议不要大于1小时, 因为JWT秘钥是无状态的
// 所以如果主密钥过期时间太长可能会导致安全问题
"expires_time": "15m",
// JWT刷新秘钥过期时间
// 该时间是在JWT主秘钥过期时间之后的时间
// 比如两者都是1h, 那么刷新秘钥的过期时间就是2h
// 因为不可能你刷新秘钥比主密钥过期还早:(
"refresh_time": "24h"
},
// SSL配置
"ssl": {
// 是否启用SSL
"enable": false,
// 是否启用HSTS
"enable_hsts": false,
// HSTS过期时间(s)
"hsts_expired_time": 5184000,
// HSTS是否包括子域名
// 警告:如果你的其他子域名没有全部部署SSL证书
// 打开此开关可能导致没有SSL证书的域名无法访问
// 如果不懂请不要打开此开关
"include_domain": false,
// SSL证书文件路径
"cert_file": "",
// SSL私钥文件路径
"key_file": ""
}
},
// gRPC服务器
"grpc_server": {
// 是否启用gRPC服务器
"enabled": false,
// gRPC服务器监听地址
"host": "0.0.0.0",
// gRPC服务器监听端口
"port": 6811,
// gRPC服务器Api缓存时间
"whazzup_cache_time": "15s"
}
},
// 数据库配置
"database": {
// 数据库类型, 支持的数据库类型: mysql, postgres, sqlite3
"type": "mysql",
// 当数据库类型为sqlite3的时候, 这里是数据库存放路径和文件名
// 反之则为要使用的数据库名称
"database": "go-fsd",
// 数据库地址
"host": "localhost",
// 数据库端口
"port": 3306,
// 数据库用户名
"username": "root",
// 数据库密码
"password": "123456",
// 是否启用SSL
"enable_ssl": false,
// 数据库连接池连接超时时间
"connect_idle_timeout": "1h",
// 连接超时时间
"connect_timeout": "5s",
// 数据库最大连接数
"server_max_connections": 32
},
// 特殊权限配置, 详情请见`特殊权限配置` 章节
"rating": {}
}
权限识别名 | 权限值 | 中文名 | 说明 |
---|---|---|---|
Ban | -1 | 封禁 | |
Normal | 0 | 普通用户 | 默认权限 |
Observer | 1 | 观察者 | |
STU1 | 2 | 放行/地面 | |
STU2 | 3 | 塔台 | |
STU3 | 4 | 终端 | |
CTR1 | 5 | 区域 | |
CTR2 | 6 | 区域 | 该权限已被弃用, 这里写出来只是为了与ES同步 |
CTR3 | 7 | 区域 | |
Instructor1 | 8 | 教员 | |
Instructor2 | 9 | 教员 | |
Instructor3 | 10 | 教员 | |
Supervisor | 11 | 监察者 | |
Administrator | 12 | 管理员 |
席位识别名 | 席位编码 | 中文名 | 说明 |
---|---|---|---|
Pilot | 1 | 飞行员 | 连线飞行员属于该席位 |
OBS | 2 | 观察者 | |
DEL | 4 | 放行 | |
GND | 8 | 地面 | |
TWR | 16 | 塔台 | |
APP | 32 | 进近 | |
CTR | 64 | 区域 | |
FSS | 128 | 飞服 |
权限识别名 | 允许的席位 | 说明 |
---|---|---|
Ban | 不允许任何席位 | 封禁用户 |
Normal | Pilot | |
Observer | Pilot,OBS | |
STU1 | Pilot,OBS,DEL,GND | |
STU2 | Pilot,OBS,DEL,GND,TWR | |
STU3 | Pilot,OBS,DEL,GND,TWR,APP | |
CTR1 | Pilot,OBS,DEL,GND,TWR,APP,CTR | |
CTR2 | Pilot,OBS,DEL,GND,TWR,APP,CTR | |
CTR3 | Pilot,OBS,DEL,GND,TWR,APP,CTR,FSS | |
Instructor1 | Pilot,OBS,DEL,GND,TWR,APP,CTR,FSS | |
Instructor2 | Pilot,OBS,DEL,GND,TWR,APP,CTR,FSS | |
Instructor3 | Pilot,OBS,DEL,GND,TWR,APP,CTR,FSS | |
Supervisor | Pilot,OBS,DEL,GND,TWR,APP,CTR,FSS | |
Administrator | Pilot,OBS,DEL,GND,TWR,APP,CTR,FSS |
你可以通过配置文件覆写管制权限与管制席位对照表
注意!!! 这个字段会覆盖
默认的对照表
所以在明确的知道你在做什么之前, 不要修改这个配置
配置文件字段为rating
{
// 特殊权限配置
"rating": {
// 键为想要修改的权限识别名的权限值
// 比如我想让Normal也可以上OBS席位, 也就是普通飞行员也可以以OBS身份连线
// Normal的权限值是0, 那我的键就是0
// 值为想要许可连线的席位的席位编码之和
// 比如我想让飞行员可以正常连线, 也可以以OBS连线
// 那么值就是 1 + 2 = 3
// 如果我想他还能上个飞服(请勿模仿)
// 那么值就是 1 + 2 + 128 = 131
// 其他权限的对照表保持为默认
// 你也可以将某个权限的值写为0来禁止使用该权限登录fsd
"0": 3
}
}
如您在使用FSD中遇到了任何/疑似bug的错误,请提交Issue 在提交时,需要您按照以下步骤进行:
- 对于可复现的bug:
- 对于不可复现的bug:
- 尽可能的用文字描述此bug并提交至Issue
MIT License
Copyright © 2025 Half_nothing
无附加条款。