first commit

This commit is contained in:
ytc1012
2025-11-18 18:08:48 +08:00
commit de90ad79ea
162 changed files with 28098 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.idea/
.vscode/
bin/

233
config/config.go Normal file
View File

@@ -0,0 +1,233 @@
package config
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"
"github.com/spf13/viper"
"gopkg.in/ini.v1"
)
var (
V *viper.Viper
ROOT string
)
const mainIniPath = "bin/conf/env.ini"
func init() {
root, err := findConfigRoot()
if err != nil {
slog.Warn("can't find config root", "error", err)
ROOT, _ = os.Getwd()
} else {
ROOT = root
}
configPath := filepath.Join(ROOT, mainIniPath)
V = viper.New()
// 使用 ini 包解析配置文件
cfg, err := ini.Load(configPath)
if err != nil {
slog.Error("load config file error", "error", err)
// 创建一个空的配置
cfg = ini.Empty()
}
// 将 ini 配置加载到 viper 中
loadIniToViper(cfg, V)
if err = loadIncludeFiles(); err != nil {
panic("load include files error:" + err.Error())
}
go signalReload()
}
func ReloadConfigFile() {
configPath := filepath.Join(ROOT, mainIniPath)
// 使用 ini 包重新加载配置文件
cfg, err := ini.Load(configPath)
if err != nil {
slog.Error("reload config file error", "error", err)
return
}
// 清空现有配置并重新加载
V = viper.New()
loadIniToViper(cfg, V)
if err := loadIncludeFiles(); err != nil {
slog.Error("reload include files error", "error", err)
return
}
slog.Info("reload config file successfully")
}
func SaveConfigFile() error {
configPath := filepath.Join(ROOT, mainIniPath)
if err := V.WriteConfigAs(configPath); err != nil {
slog.Error("save config file error", "error", err)
return err
}
slog.Info("save config file successfully")
return nil
}
func loadIncludeFiles() error {
includeFile := GetString("include_files.path", "")
if includeFile != "" {
includeFiles := strings.Split(includeFile, ",")
for _, file := range includeFiles {
file = strings.TrimSpace(file)
if file != "" {
// 使用绝对路径或相对路径
if !filepath.IsAbs(file) {
file = filepath.Join(ROOT, file)
}
// 使用 ini 包加载包含的配置文件
cfg, err := ini.Load(file)
if err != nil {
return fmt.Errorf("failed to read config file %s: %w", file, err)
}
// 合并配置到主 viper 实例
loadIniToViper(cfg, V)
}
}
}
return nil
}
// GetString 获取字符串配置值,支持 section.key 格式
func GetString(key string, defaultValue string) string {
if V.IsSet(key) {
return V.GetString(key)
}
return defaultValue
}
// GetInt 获取整数配置值,支持 section.key 格式
func GetInt(key string, defaultValue int) int {
if V.IsSet(key) {
return V.GetInt(key)
}
return defaultValue
}
// GetBool 获取布尔配置值,支持 section.key 格式
func GetBool(key string, defaultValue bool) bool {
if V.IsSet(key) {
return V.GetBool(key)
}
return defaultValue
}
// GetSection 获取整个 section 的配置,返回 map[string]string
func GetSection(section string) (map[string]string, error) {
// 从 viper 中获取 section格式为 section.key
sectionMap := make(map[string]string)
// 遍历所有配置键,查找属于该 section 的配置
allKeys := V.AllKeys()
found := false
for _, key := range allKeys {
if strings.HasPrefix(key, section+".") {
found = true
subKey := strings.TrimPrefix(key, section+".")
sectionMap[subKey] = V.GetString(key)
}
}
if !found {
return nil, fmt.Errorf("section %s not found", section)
}
return sectionMap, nil
}
// GetPath 获取路径配置值,如果是相对路径则基于 ROOT 解析为绝对路径
func GetPath(key string, defaultValue string) string {
pathValue := GetString(key, defaultValue)
if pathValue == "" {
return defaultValue
}
// 如果是绝对路径,直接返回
if filepath.IsAbs(pathValue) {
return pathValue
}
// 相对路径基于 ROOT 解析
return filepath.Join(ROOT, pathValue)
}
// loadIniToViper 将 ini 配置加载到 viper 中
func loadIniToViper(cfg *ini.File, v *viper.Viper) {
for _, section := range cfg.Sections() {
sectionName := section.Name()
// 跳过默认 section (DEFAULT)
if sectionName == ini.DEFAULT_SECTION {
sectionName = ""
}
for _, key := range section.Keys() {
keyName := key.Name()
value := key.Value()
// 构建 viper 键名section.key 或 key (如果 section 为空)
var viperKey string
if sectionName != "" {
viperKey = sectionName + "." + keyName
} else {
viperKey = keyName
}
v.Set(viperKey, value)
}
}
}
// fileExist 检查文件或目录是否存在
// 如果由 filename 指定的文件或目录存在则返回 true否则返回 false
func fileExist(filename string) bool {
_, err := os.Stat(filename)
return err == nil || os.IsExist(err)
}
func findConfigRoot() (string, error) {
if execPath, err := os.Executable(); err == nil {
if root := searchConfig(filepath.Dir(execPath)); root != "" {
return root, nil
}
}
if wd, err := os.Getwd(); err == nil {
if root := searchConfig(wd); root != "" {
return root, nil
}
}
return "", fmt.Errorf("unable to locate %s", mainIniPath)
}
func searchConfig(start string) string {
dir := start
for {
configPath := filepath.Join(dir, mainIniPath)
if fileExist(configPath) {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
return ""
}

22
config/reload_unix.go Normal file
View File

@@ -0,0 +1,22 @@
// +build !windows,!plan9
package config
import (
"os"
"os/signal"
"syscall"
)
func signalReload() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGUSR1)
for {
sig := <-ch
switch sig {
case syscall.SIGUSR1:
ReloadConfigFile()
}
}
}

4
config/reload_windows.go Normal file
View File

@@ -0,0 +1,4 @@
package config
func signalReload() {
}

73
constant/code.go Normal file
View File

@@ -0,0 +1,73 @@
package constant
const ProxyConnectError = -4 //代理连接失败
const ProxyNotInConnect = -3 //代理错误
const UserNotInConnect = -2 //链接没有找到用户
const RoleNotInConnect = -1 //链接没有找到角色
const OK = 0
const InvalidParam = 1 //参数有误
const DBError = 2 //数据库异常
const UserExist = 3 //用户已存在
const PwdIncorrect = 4 //密码不正确
const UserNotExist = 5 //用户不存在
const SessionInvalid = 6 //session无效
const HardwareIncorrect = 7 //Hardware错误
const RoleAlreadyCreate = 8 //已经创建过角色了
const RoleNotExist = 9 //角色不存在
const CityNotExist = 10 //城市不存在
const CityNotMe = 11 //城市不是自己的
const UpError = 12 //升级失败
const GeneralNotFound = 13 //武将不存在
const GeneralNotMe = 14 //武将不是自己的
const ArmyNotFound = 15 //军队不存在
const ArmyNotMe = 16 //军队不是自己的
const ResNotEnough = 17 //资源不足
const OutArmyLimit = 18 //超过带兵限制
const ArmyBusy = 19 //军队再忙
const GeneralBusy = 20 //将领再忙
const CannotGiveUp = 21 //不能放弃
const BuildNotMe = 22 //领地不是自己的
const ArmyNotMain = 23 //军队没有主将
const UnReachable = 24 //不可到达
const PhysicalPowerNotEnough = 25 //体力不足
const DecreeNotEnough = 26 //政令不足
const GoldNotEnough = 27 //金币不足
const GeneralRepeat = 28 //重复上阵
const CostNotEnough = 29 //cost不足
const GeneralNoHas = 30 //没有该合成武将
const GeneralNoSame = 31 //合成武将非同名
const ArmyNotEnough = 32 //队伍数不足
const TongShuaiNotEnough = 33 //统帅不足
const GeneralStarMax = 34 //升级到最大星级
const UnionCreateError = 35 //联盟创建失败
const UnionNotFound = 36 //联盟不存在
const PermissionDenied = 37 //权限不足
const UnionAlreadyHas = 38 //已经有联盟
const UnionNotAllowExit = 39 //不允许退出
const ContentTooLong = 40 //内容太长
const NotBelongUnion = 41 //不属于该联盟
const PeopleIsFull = 42 //用户已满
const HasApply = 43 //已经申请过了
const BuildCanNotDefend = 44 //不能驻守
const BuildCanNotAttack = 45 //不能占领
const BuildMBSNotFound = 46 //没有军营
const BuildWarFree = 47 //免战中
const ArmyConscript = 48 //征兵中
const BuildGiveUpAlready = 49 //领地已经在放弃了
const CanNotBuildNew = 50 //不能再新建建筑在领地上
const CanNotTransfer = 51 //不能调兵
const HoldIsFull = 52 //坑位已满
const ArmyIsOutside = 53 //队伍在城外
const CanNotUpBuild = 54 //不能升级建筑
const CanNotDestroy = 55 //不能拆除建筑
const OutCollectTimesLimit = 56 //超过征收次数
const InCdCanNotOperate = 57 //cd内不能操作
const OutGeneralLimit = 58 //武将超过上限了
const NotHasJiShi = 59 //没有集市
const OutPosTagLimit = 60 //超过了收藏上限
const OutSkillLimit = 61 //超过了技能上限
const UpSkillError = 62 //装备技能失败
const DownSkillError = 63 //取下技能失败
const OutArmNotMatch = 64 //兵种不符
const PosNotSkill = 65 //该位置没有技能
const SkillLevelFull = 66 //技能等级已满

233
data/conf/db.sql Normal file
View File

@@ -0,0 +1,233 @@
CREATE TABLE IF NOT EXISTS `tb_user_info` (
`uid` int unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL COMMENT '用户名',
`passcode` char(12) NOT NULL DEFAULT '' COMMENT '加密随机数',
`passwd` char(64) NOT NULL DEFAULT '' COMMENT 'md5密码',
`status` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '用户账号状态。0-默认1-冻结2-停号',
`hardware` varchar(64) NOT NULL DEFAULT '' COMMENT 'hardware',
`ctime` timestamp NOT NULL DEFAULT '2013-03-15 14:38:09',
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`uid`),
UNIQUE KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户信息表';
CREATE TABLE IF NOT EXISTS `tb_login_history` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`uid` int unsigned NOT NULL DEFAULT 0 COMMENT '用户UID',
`state` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '登录状态0登录1登出',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '登录时间',
`ip` varchar(31) NOT NULL DEFAULT '' COMMENT 'ip',
`hardware` varchar(64) NOT NULL DEFAULT '' COMMENT 'hardware',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '用户登录表';
CREATE TABLE IF NOT EXISTS `tb_login_last` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`uid` int unsigned NOT NULL DEFAULT 0 COMMENT '用户UID',
`login_time` timestamp COMMENT '登录时间',
`logout_time` timestamp COMMENT '登出时间',
`ip` varchar(31) NOT NULL DEFAULT '' COMMENT 'ip',
`is_logout` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否logout,1:logout0:login',
`session` varchar(100) COMMENT '会话',
`hardware` varchar(64) NOT NULL DEFAULT '' COMMENT 'hardware',
UNIQUE KEY (`uid`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '最后一次用户登录表';
CREATE TABLE IF NOT EXISTS `tb_role_1` (
`rid` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'roleId',
`uid` int unsigned NOT NULL COMMENT '用户UID',
`headId` int unsigned NOT NULL DEFAULT 0 COMMENT '头像Id',
`sex` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '性别0:女 1男',
`nick_name` varchar(100) COMMENT 'nick_name',
`balance` int unsigned NOT NULL DEFAULT 0 COMMENT '余额',
`login_time` timestamp COMMENT '登录时间',
`logout_time` timestamp COMMENT '登出时间',
`profile` varchar(500) COMMENT '个人简介',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY (`uid`),
PRIMARY KEY (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '玩家表';
CREATE TABLE IF NOT EXISTS `tb_map_role_city_1` (
`cityId` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'cityId',
`rid` int unsigned NOT NULL COMMENT 'roleId',
`x` int unsigned NOT NULL COMMENT 'x坐标',
`y` int unsigned NOT NULL COMMENT 'y坐标',
`name` varchar(100) NOT NULL DEFAULT '城池' COMMENT '城池名称',
`is_main` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否是主城',
`cur_durable` int unsigned NOT NULL COMMENT '当前耐久',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`occupy_time` timestamp DEFAULT '2013-03-15 14:38:09' COMMENT '占领时间',
PRIMARY KEY (`cityId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '玩家城池';
CREATE TABLE IF NOT EXISTS `tb_map_role_build_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`rid` int unsigned NOT NULL,
`type` int unsigned NOT NULL COMMENT '建筑类型',
`level` tinyint unsigned NOT NULL COMMENT '建筑等级',
`op_level` tinyint unsigned COMMENT '建筑操作等级',
`x` int unsigned NOT NULL COMMENT 'x坐标',
`y` int unsigned NOT NULL COMMENT 'y坐标',
`name` varchar(100) NOT NULL COMMENT '名称',
`max_durable` int unsigned NOT NULL COMMENT '最大耐久',
`cur_durable` int unsigned NOT NULL COMMENT '当前耐久',
`end_time` timestamp DEFAULT '2013-03-15 14:38:09' COMMENT '建造、升级、拆除结束时间',
`occupy_time` timestamp DEFAULT '2013-03-15 14:38:09' COMMENT '占领时间',
`giveUp_time` int unsigned DEFAULT '0' COMMENT '放弃时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '角色建筑';
CREATE TABLE IF NOT EXISTS `tb_city_facility_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`cityId` int unsigned NOT NULL COMMENT '城市id',
`rid` int unsigned NOT NULL,
`facilities` varchar(4096) NOT NULL COMMENT '设施列表格式为json结构',
UNIQUE KEY (`cityId`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '城池设施';
CREATE TABLE IF NOT EXISTS `tb_role_res_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`rid` int unsigned NOT NULL COMMENT 'rid',
`wood` int unsigned NOT NULL COMMENT '',
`iron` int unsigned NOT NULL COMMENT '',
`stone` int unsigned NOT NULL COMMENT '石头',
`grain` int unsigned NOT NULL COMMENT '粮食',
`gold` int unsigned NOT NULL COMMENT '金币',
`decree` int unsigned NOT NULL COMMENT '令牌',
UNIQUE KEY (`rid`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '角色资源表';
CREATE TABLE IF NOT EXISTS `tb_general_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`rid` int unsigned NOT NULL COMMENT 'rid',
`cfgId` int unsigned NOT NULL COMMENT '配置id',
`physical_power` int unsigned NOT NULL COMMENT '体力',
`exp` int unsigned NOT NULL COMMENT '经验',
`order` tinyint NOT NULL COMMENT '第几队',
`level` tinyint unsigned NOT NULL DEFAULT 1 COMMENT 'level',
`cityId` int NOT NULL DEFAULT 0 COMMENT '城市id',
`star` int NOT NULL DEFAULT 0 COMMENT '稀有度(星级)',
`star_lv` int NOT NULL DEFAULT 0 COMMENT '稀有度(星级)进阶等级级',
`arms` int NOT NULL DEFAULT 0 COMMENT '兵种',
`has_pr_point` int NOT NULL DEFAULT 0 COMMENT '总属性点',
`use_pr_point` int NOT NULL DEFAULT 0 COMMENT '已用属性点',
`attack_distance` int NOT NULL DEFAULT 0 COMMENT '攻击距离',
`force_added` int NOT NULL DEFAULT 0 COMMENT '已加攻击属性',
`strategy_added` int NOT NULL DEFAULT 0 COMMENT '已加战略属性',
`defense_added` int NOT NULL DEFAULT 0 COMMENT '已加防御属性',
`speed_added` int NOT NULL DEFAULT 0 COMMENT '已加速度属性',
`destroy_added` int NOT NULL DEFAULT 0 COMMENT '已加破坏属性',
`parentId` int NOT NULL DEFAULT 0 COMMENT '已合成到武将的id',
`compose_type` int NOT NULL DEFAULT 0 COMMENT '合成类型',
`skills` varchar(64) NOT NULL DEFAULT '[0, 0, 0]' COMMENT '携带的技能',
`state` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '0:正常1:转换掉了',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '将领表';
CREATE TABLE IF NOT EXISTS `tb_army_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`rid` int unsigned NOT NULL COMMENT 'rid',
`cityId` int unsigned NOT NULL COMMENT '城市id',
`order` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '第几队 1-5队',
`generals` varchar(256) NOT NULL DEFAULT '[0, 0, 0]' COMMENT '将领',
`soldiers` varchar(256) NOT NULL DEFAULT '[0, 0, 0]' COMMENT '士兵',
`conscript_times` varchar(256) NOT NULL DEFAULT '[0, 0, 0]' COMMENT '征兵结束时间',
`conscript_cnts` varchar(256) NOT NULL DEFAULT '[0, 0, 0]' COMMENT '征兵数量',
`cmd` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '命令 0:空闲 1:攻击 2驻军 3:返回',
`from_x` int unsigned NOT NULL COMMENT '来自x坐标',
`from_y` int unsigned NOT NULL COMMENT '来自y坐标',
`to_x` int unsigned COMMENT '去往x坐标',
`to_y` int unsigned COMMENT '去往y坐标',
`start` timestamp COMMENT '出发时间',
`end` timestamp COMMENT '到达时间',
UNIQUE KEY (`rid`, `cityId`, `order`),
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '军队表';
CREATE TABLE IF NOT EXISTS `tb_war_report_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`a_rid` int unsigned NOT NULL COMMENT '攻击方id',
`d_rid` int unsigned NOT NULL DEFAULT 0 COMMENT '防守方id,0为系统npc',
`b_a_army` varchar(512) NOT NULL COMMENT '开始攻击方军队',
`b_d_army` varchar(512) NOT NULL COMMENT '开始防守方军队',
`e_a_army` varchar(512) NOT NULL COMMENT '开始攻击方军队',
`e_d_army` varchar(512) NOT NULL COMMENT '开始防守方军队',
`b_a_general` varchar(512) NOT NULL COMMENT '开始攻击方武将',
`b_d_general` varchar(512) NOT NULL COMMENT '开始防守方武将',
`e_a_general` varchar(512) NOT NULL COMMENT '结束攻击方武将',
`e_d_general` varchar(512) NOT NULL COMMENT '结束防守方武将',
`rounds` varchar(1024) NOT NULL COMMENT '回合战报数据',
`result` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '0失败1打平2胜利',
`a_is_read` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '攻击方战报是否已阅 0:未阅 1:已阅',
`d_is_read` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '攻击方战报是否已阅 0:未阅 1:已阅',
`destroy` int unsigned COMMENT '破坏了多少耐久',
`occupy` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否攻占 0:否 1:是',
`x` int unsigned COMMENT 'x坐标',
`y` int unsigned COMMENT 'y坐标',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '战报表';
CREATE TABLE IF NOT EXISTS `tb_coalition_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(20) NOT NULL COMMENT '联盟名字',
`members` varchar(2048) NOT NULL COMMENT '成员',
`create_id` int unsigned NOT NULL COMMENT '创建者id',
`chairman` int unsigned NOT NULL COMMENT '盟主',
`vice_chairman` int unsigned NOT NULL COMMENT '副盟主',
`notice` varchar(256) COMMENT '公告',
`state` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '0解散1运行中',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY (`name`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '联盟';
CREATE TABLE IF NOT EXISTS `tb_coalition_apply_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`union_id` int unsigned NOT NULL COMMENT '联盟id',
`rid` int unsigned NOT NULL COMMENT '申请者的rid',
`state` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '申请状态0未处理1拒绝2通过',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '联盟申请表';
CREATE TABLE IF NOT EXISTS `tb_role_attribute_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`rid` int unsigned NOT NULL COMMENT 'rid',
`parent_id` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '上级联盟id',
`collect_times` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '征收次数',
`last_collect_time` timestamp DEFAULT '2013-03-15 14:38:09' COMMENT '最后征收时间',
`pos_tags` varchar(512) COMMENT '收藏的位置',
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '玩家属性表';
CREATE TABLE IF NOT EXISTS `tb_coalition_log_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`union_id` int unsigned NOT NULL COMMENT '联盟id',
`op_rid` int unsigned NOT NULL COMMENT '操作者id',
`target_id` int unsigned COMMENT '被操作的对象',
`des` varchar(256) NOT NULL COMMENT '描述',
`state` tinyint unsigned NOT NULL COMMENT '0:创建,1:解散,2:加入,3:退出,4:踢出,5:任命,6:禅让,7:修改公告',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '发生时间',
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '联盟日志表';
CREATE TABLE IF NOT EXISTS `tb_skill_1` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'id',
`rid` int unsigned NOT NULL COMMENT 'rid',
`cfgId` int unsigned NOT NULL COMMENT '技能id',
`belong_generals` varchar(256) NOT NULL Default '[]' COMMENT '归属武将数组',
`ctime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '获得技能时间',
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '技能表';

56
data/conf/env.ini Normal file
View File

@@ -0,0 +1,56 @@
[mysql]
host = 127.0.0.1
port = 3306
user = root
password = root
dbname = slgdb
charset = utf8
; 最大空闲连接数
max_idle = 2
; 最大打开连接数
max_conn = 10
[httpserver]
host =
port = 8088
[gateserver]
host =
port = 8004
need_secret = true
slg_proxy = ws://127.0.0.1:8001
chat_proxy = ws://127.0.0.1:8002
login_proxy = ws://127.0.0.1:8003
[slgserver]
host =
port = 8001
need_secret = false
[chatserver]
host =
port = 8002
need_secret = false
[loginserver]
host =
port = 8003
need_secret = false
[xorm]
show_sql = true
log_file = ../logs/sql.log
[log]
file_dir = ../logs/
max_size = 120
max_backups = 100
max_age = 10
compress = true
[logic]
map_data = data/conf/mapRes_0.json
json_data = data/conf/json/
server_id = 1

96
data/conf/json/basic.json Normal file
View File

@@ -0,0 +1,96 @@
{
"conscript": {
"des": "每征一个兵需要消耗的资源",
"cost_wood":10,
"cost_iron":10,
"cost_stone":0,
"cost_grain":10,
"cost_gold":1,
"cost_time":1
},
"general": {
"des": "武将的一些配置",
"physical_power_limit": 100,
"cost_physical_power": 1,
"recovery_physical_power": 10,
"reclamation_time": 30,
"reclamation_cost": 1,
"draw_general_cost": 30,
"pr_point": 1000,
"limit": 500
},
"role": {
"des": "角色的一些配置",
"wood": 1000000,
"iron": 1000000,
"stone": 1000000,
"grain": 1000000,
"gold": 1000000,
"decree": 20,
"wood_yield": 1000000,
"iron_yield": 1000000,
"stone_yield": 1000000,
"grain_yield": 1000000,
"gold_yield": 1000000,
"depot_capacity": 100000000000,
"build_limit": 20,
"recovery_time": 20,
"decree_limit": 20,
"collect_times_limit": 3,
"collect_interval": 30,
"pos_tag_limit": 10
},
"city": {
"des": "城池的一些配置",
"cost": 75,
"durable": 100000,
"transform_rate": 50,
"recovery_time": 600
},
"build": {
"des": "建筑的一些配置",
"war_free": 20,
"giveUp_time": 30,
"fortress_limit": 10
},
"npc": {
"des": "npc一些配置",
"levels": [
{
"soilders": 100
},
{
"soilders": 200
},
{
"soilders": 300
},
{
"soilders": 400
},
{
"soilders": 500
},
{
"soilders": 600
},
{
"soilders": 700
},
{
"soilders": 800
},
{
"soilders": 900
},
{
"soilders": 1000
}
]
},
"union": {
"des": "联盟的一些配置",
"member_limit": 100
}
}

View File

@@ -0,0 +1,109 @@
{
"title": "城内设施类型",
"list": [
{
"type": 0,
"name": "主城"
},
{
"type": 1,
"name": "疾风营"
},
{
"type": 2,
"name": "铁壁营"
},
{
"type": 3,
"name": "军机营"
},
{
"type": 4,
"name": "尚武营"
},
{
"type": 5,
"name": "军营"
},
{
"type": 6,
"name": "预备役所"
},
{
"type": 7,
"name": "汉点将台"
},
{
"type": 8,
"name": "魏点将台"
},
{
"type": 9,
"name": "蜀点将台"
},
{
"type": 10,
"name": "吴点将台"
},
{
"type": 11,
"name": "群点将台"
},
{
"type": 12,
"name": "封禅台"
},
{
"type": 13,
"name": "校场"
},
{
"type": 14,
"name": "统帅厅"
},
{
"type": 15,
"name": "集市"
},
{
"type": 16,
"name": "募兵所"
},
{
"type": 17,
"name": "伐木场"
},
{
"type": 18,
"name": "炼铁场"
},
{
"type": 19,
"name": "磨坊"
},
{
"type": 20,
"name": "采石场"
},
{
"type": 21,
"name": "民居"
},
{
"type": 22,
"name": "社稷坛"
},
{
"type": 23,
"name": "城墙"
},
{
"type": 24,
"name": "女墙"
},
{
"type": 25,
"name": "仓库"
}
]
}

View File

@@ -0,0 +1,131 @@
{
"title": "城池设施加成类别",
"list": [
{
"type": 1,
"des": "耐久度上限",
"value": "+%n%"
},
{
"type": 2,
"des": "部队统帅上限",
"value": "+%n%"
},
{
"type": 3,
"des": "可配置队伍数",
"value": "%n%"
},
{
"type": 4,
"des": "部队速度",
"value": "+%n%"
},
{
"type": 5,
"des": "部队防御",
"value": "+%n%"
},
{
"type": 6,
"des": "部队谋略",
"value": "+%n%"
},
{
"type": 7,
"des": "部队攻击",
"value": "+%n%"
},
{
"type": 8,
"des": "征兵时间",
"value": "-%n%%"
},
{
"type": 9,
"des": "预备役上限",
"value": "+%n%"
},
{
"type": 10,
"des": "金币",
"value": "-%n%%"
},
{
"type": 11,
"des": "汉阵营加成",
"value": "+%n%"
},
{
"type": 12,
"des": "群阵营加成",
"value": "+%n%"
},
{
"type": 13,
"des": "魏阵营加成",
"value": "+%n%"
},
{
"type": 14,
"des": "蜀阵营加成",
"value": "+%n%"
},
{
"type": 15,
"des": "吴阵营加成",
"value": "+%n%"
},
{
"type": 16,
"des": "交易兑换率",
"value": "+%n%%"
},
{
"type": 17,
"des": "木材产量",
"value": "+%n%/小时"
},
{
"type": 18,
"des": "铁矿产量",
"value": "+%n%/小时"
},
{
"type": 19,
"des": "粮食产量",
"value": "+%n%/小时"
},
{
"type": 20,
"des": "石矿产量",
"value": "+%n%/小时"
},
{
"type": 21,
"des": "税收",
"value": "+%n%"
},
{
"type": 22,
"des": "扩建次数",
"value": "+%n%"
},
{
"type": 23,
"des": "仓库容量",
"value": "+%n%"
},
{
"type": 24,
"des": "带兵数量",
"value": "+%n%"
},
{
"type": 25,
"des": "可配置前锋部队数量",
"value": "%n%"
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-疾风营配置表",
"name": "疾风营",
"type": 1,
"des": "提高本城部队速度",
"additions": [
4
],
"conditions": [
{
"type": 0,
"level": 2
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-军机营配置表",
"name": "军机营",
"type": 3,
"des": "提高本城部队谋略",
"additions": [
6
],
"conditions": [
{
"type": 0,
"level": 3
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-尚武营配置表",
"name": "尚武营",
"type": 4,
"des": "提高本城部队攻击",
"additions": [
7
],
"conditions": [
{
"type": 0,
"level": 3
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-铁壁营配置表",
"name": "铁壁营",
"type": 2,
"des": "提高本城部队防御",
"additions": [
5
],
"conditions": [
{
"type": 0,
"level": 2
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-兵营配置表",
"name": "兵营",
"type": 5,
"des": "提高本城部队武将带兵数量",
"additions": [
24
],
"conditions": [
{
"type": 0,
"level": 7
}
],
"levels": [
{
"level": 1,
"values": [
100
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
200
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
300
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
400
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
500
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,91 @@
{
"title": "城内设施-预备役配置表",
"name": "预备役所",
"type": 6,
"des": "提高本城预备役上限",
"additions": [
9
],
"conditions": [
{
"type": 0,
"level": 6
},
{
"type": 13,
"level": 2
}
],
"levels": [
{
"level": 1,
"values": [
1000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-汉点将台配置表",
"name": "汉点将台",
"type": 7,
"des": "提高汉阵营加成效果",
"additions": [
11
],
"conditions": [
{
"type": 0,
"level": 4
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 10
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,99 @@
{
"title": "城内设施-群点将台配置表",
"name": "群点将台",
"type": 11,
"des": "提高群阵营加成效果",
"additions": [
12
],
"conditions": [
{
"type": 0,
"level": 6
},
{
"type": 8,
"level": 1
},
{
"type": 9,
"level": 1
},
{
"type": 10,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,91 @@
{
"title": "城内设施-蜀点将台配置表",
"name": "蜀点将台",
"type": 9,
"des": "提高蜀阵营加成效果",
"additions": [
14
],
"conditions": [
{
"type": 0,
"level": 5
},
{
"type": 7,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,91 @@
{
"title": "城内设施-魏点将台配置表",
"name": "魏点将台",
"type": 8,
"des": "提高魏阵营加成效果",
"additions": [
13
],
"conditions": [
{
"type": 0,
"level": 5
},
{
"type": 7,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,91 @@
{
"title": "城内设施-吴点将台配置表",
"name": "吴点将台",
"type": 10,
"des": "提高吴阵营加成效果",
"additions": [
15
],
"conditions": [
{
"type": 0,
"level": 5
},
{
"type": 7,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,163 @@
{
"title": "主城配置",
"name": "主城",
"type": 0,
"des": "提高本城的耐久上限部队统帅上限",
"additions": [
1,
2
],
"conditions": [],
"levels": [
{
"level": 1,
"values": [
10000,
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
20000,
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
30000,
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
40000,
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
50000,
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
60000,
30
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
70000,
35
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
80000,
40
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
90000,
45
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
100000,
50
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,91 @@
{
"title": "城内设施-封禅台配置",
"name": "封禅台",
"type": 12,
"des": "提高本城部队的统帅上限",
"additions": [
2
],
"conditions": [
{
"type": 0,
"level": 8
},
{
"type": 5,
"level": 5
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-校场配置表",
"name": "校场",
"type": 13,
"des": "使本城可以配置部队,并提高本城可配置部队数量",
"additions": [
3
],
"conditions": [
{
"type": 0,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
1
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-统帅厅配置表",
"name": "统帅厅",
"type": 14,
"des": "使本城可以配置前锋,并提高本城可配置前锋部队数量",
"additions": [
25
],
"conditions": [
{
"type": 16,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
1
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,157 @@
{
"title": "城内设施-集市配置表",
"des": "提高交易兑换率",
"name": "集市",
"type": 15,
"additions": [
16
],
"conditions": [
{
"type": 0,
"level": 5
}
],
"levels": [
{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
30
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
35
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
40
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
45
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
50
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,160 @@
{
"title": "募兵所配置表",
"des": "开启本城部队征兵,并缩短征兵时间",
"name": "募兵所",
"type": 16,
"additions": [
10
],
"conditions": [
{
"type": 0,
"level": 2
},
{
"type": 13,
"level": 2
}
],
"levels": [{
"level": 1,
"values": [
5
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
10
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
15
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
20
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
25
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
30
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
35
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
40
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
45
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
50
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,157 @@
{
"title": "城内设施-采石场配置表",
"name": "采石场",
"des": "额外获得石矿产量",
"type": 20,
"additions": [
20
],
"conditions": [
{
"type": 0,
"level": 3
}
],
"levels": [
{
"level": 1,
"values": [
1000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
6000
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
7000
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
8000
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
9000
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,157 @@
{
"title": "城内设施-伐木场配置表",
"name": "伐木场",
"des": "额外获得木材产量",
"type": 17,
"additions": [
17
],
"conditions": [
{
"type": 0,
"level": 2
}
],
"levels": [
{
"level": 1,
"values": [
1000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
6000
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
7000
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
8000
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
9000
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,157 @@
{
"title": "城内设施-炼铁场配置表",
"name": "炼铁场",
"des": "额外获得铁矿产量",
"type": 18,
"additions": [
18
],
"conditions": [
{
"type": 0,
"level": 2
}
],
"levels": [
{
"level": 1,
"values": [
1000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
6000
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
7000
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
8000
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
9000
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,157 @@
{
"title": "城内设施-磨坊配置表",
"name": "磨坊",
"des": "额外获得粮食产量",
"type": 19,
"additions": [
19
],
"conditions": [
{
"type": 0,
"level": 3
}
],
"levels": [
{
"level": 1,
"values": [
1000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
6000
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
7000
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
8000
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
9000
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,157 @@
{
"title": "城内设施-民居配置表",
"name": "民居",
"des": "提供税收",
"type": 21,
"additions": [
21
],
"conditions": [
{
"type": 0,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
1000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
6000
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
7000
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
8000
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
},
{
"level": 9,
"values": [
9000
],
"need": {
"decree": 0,
"grain": 9000,
"wood": 9000,
"iron": 9000,
"stone": 9000
},
"time": 90
},
{
"level": 10,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 10000,
"wood": 10000,
"iron": 10000,
"stone": 10000
},
"time": 100
}
]
}

View File

@@ -0,0 +1,128 @@
{
"title": "城内设施-社稷坛配置",
"name": "社稷坛",
"type": 22,
"des": "每升一级可以额外扩建一次",
"additions": [
22
],
"conditions": [
{
"type": 0,
"level": 9
}
],
"levels": [{
"level": 1,
"values": [
1
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
2
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
3
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
4
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
5
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
6
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
7
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
8
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-城墙配置",
"name": "城墙",
"type": 23,
"des": "提高本城耐久上限",
"additions": [
1
],
"conditions": [
{
"type": 0,
"level": 7
}
],
"levels": [
{
"level": 1,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
20000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
30000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
40000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
50000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,87 @@
{
"title": "城内设施-女墙配置",
"name": "女墙",
"type": 24,
"des": "提高本城耐久上限",
"additions": [
1
],
"conditions": [
{
"type": 23,
"level": 1
}
],
"levels": [
{
"level": 1,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
20000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
30000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
40000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
50000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
}
]
}

View File

@@ -0,0 +1,129 @@
{
"title": "城内设施-仓库配置",
"name": "仓库",
"type": 25,
"des": "每升一级增加仓库容量",
"additions": [
23
],
"conditions": [
{
"type": 0,
"level": 2
}
],
"levels": [
{
"level": 1,
"values": [
10000
],
"need": {
"decree": 0,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"time": 10
},
{
"level": 2,
"values": [
20000
],
"need": {
"decree": 0,
"grain": 2000,
"wood": 2000,
"iron": 2000,
"stone": 2000
},
"time": 20
},
{
"level": 3,
"values": [
30000
],
"need": {
"decree": 0,
"grain": 3000,
"wood": 3000,
"iron": 3000,
"stone": 3000
},
"time": 30
},
{
"level": 4,
"values": [
40000
],
"need": {
"decree": 0,
"grain": 4000,
"wood": 4000,
"iron": 4000,
"stone": 4000
},
"time": 40
},
{
"level": 5,
"values": [
50000
],
"need": {
"decree": 0,
"grain": 5000,
"wood": 5000,
"iron": 5000,
"stone": 5000
},
"time": 50
},
{
"level": 6,
"values": [
60000
],
"need": {
"decree": 0,
"grain": 6000,
"wood": 6000,
"iron": 6000,
"stone": 6000
},
"time": 60
},
{
"level": 7,
"values": [
70000
],
"need": {
"decree": 0,
"grain": 7000,
"wood": 7000,
"iron": 7000,
"stone": 7000
},
"time": 70
},
{
"level": 8,
"values": [
80000
],
"need": {
"decree": 0,
"grain": 8000,
"wood": 8000,
"iron": 8000,
"stone": 8000
},
"time": 80
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,117 @@
{
"title": "兵种配置表",
"arms":[
{
"id": 1,
"name": "步兵",
"condition":{
"level": 1,
"star_lv": 0
},
"change_cost": {
"gold": 1000
},
"harm_ratio": [100, 110, 90, 95, 105, 85, 90, 100, 80]
},
{
"id": 2,
"name": "弓兵",
"condition":{
"level": 1,
"star_lv": 0
},
"change_cost": {
"gold": 1000
},
"harm_ratio":[90, 100, 110, 85, 95, 105, 80, 90, 100]
},
{
"id": 3,
"name": "骑兵",
"condition":{
"level": 1,
"star_lv": 0
},
"change_cost": {
"gold": 1000
},
"harm_ratio": [110, 90, 100, 105, 85, 95, 100, 80, 90]
},
{
"id": 4,
"name": "轻步兵",
"condition":{
"level": 3,
"star_lv": 0
},
"change_cost": {
"gold": 5000
},
"harm_ratio": [105, 115, 95, 100, 110, 90, 95, 105, 85]
},
{
"id": 5,
"name": "长弓兵",
"condition":{
"level": 3,
"star_lv": 0
},
"change_cost": {
"gold": 6000
},
"harm_ratio": [95, 105, 115, 90, 100, 110, 85, 95, 105]
},
{
"id": 6,
"name": "轻骑兵",
"condition":{
"level": 3,
"star_lv": 0
},
"change_cost": {
"gold": 4000
},
"harm_ratio": [115, 95, 105, 110, 90, 100, 105, 85, 95]
},
{
"id": 7,
"name": "重甲兵",
"condition":{
"level": 7,
"star_lv": 0
},
"change_cost": {
"gold": 14000
},
"harm_ratio": [110, 120, 90, 105, 115, 95, 100, 110, 90]
},
{
"id": 8,
"name": "弩兵",
"condition":{
"level": 3,
"star_lv": 0
},
"change_cost": {
"gold": 6000
},
"harm_ratio": [100, 110, 120, 95, 105, 115, 90, 100, 110]
},
{
"id": 9,
"name": "重骑兵",
"condition":{
"level": 7,
"star_lv": 0
},
"change_cost": {
"gold": 18000
},
"harm_ratio": [120, 100, 110, 105, 95, 105, 110, 90, 100]
}
]
}

View File

@@ -0,0 +1,105 @@
{
"title": "将领基本配置表",
"levels":[
{
"level": 1,
"exp": 0,
"soldiers": 100
},
{
"level": 2,
"exp": 1000,
"soldiers": 200
},
{
"level": 3,
"exp": 2000,
"soldiers": 300
},
{
"level": 4,
"exp": 3000,
"soldiers": 400
},
{
"level": 5,
"exp": 4000,
"soldiers": 500
},
{
"level": 6,
"exp": 5000,
"soldiers": 600
},
{
"level": 7,
"exp": 6000,
"soldiers": 700
},
{
"level": 8,
"exp": 7500,
"soldiers": 800
},
{
"level": 9,
"exp": 9000,
"soldiers": 900
},
{
"level": 10,
"exp": 11000,
"soldiers": 1000
},
{
"level": 11,
"exp": 12500,
"soldiers": 1100
},
{
"level": 12,
"exp": 15000,
"soldiers": 1200
},
{
"level": 13,
"exp": 17000,
"soldiers": 1300
},
{
"level": 14,
"exp": 19000,
"soldiers": 1400
},
{
"level": 15,
"exp": 23000,
"soldiers": 1500
}, {
"level": 16,
"exp": 26000,
"soldiers": 1600
},
{
"level": 17,
"exp": 29000,
"soldiers": 1700
},
{
"level": 18,
"exp": 31000,
"soldiers": 1800
},
{
"level": 19,
"exp": 35000,
"soldiers": 1900
},
{
"level": 20,
"exp": 50000,
"soldiers": 2000
}
]
}

View File

@@ -0,0 +1,439 @@
{
"title": "地图资源配置",
"cfg":[
{"type": 50,
"des": "系统要塞",
"name": "要塞",
"level": 5,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 30000,
"defender": 1
},
{"type": 51,
"name": "城区",
"level": 1,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 100000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 2,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 200000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 3,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 300000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 4,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 400000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 5,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 500000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 6,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 600000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 7,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 700000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 8,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 800000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 9,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 900000,
"defender": 5
},
{"type": 51,
"name": "城区",
"level": 10,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 1000000,
"defender": 5
},
{"type": 52,
"name": "土地Lv.1",
"level": 1,
"grain": 100,
"wood": 100,
"iron": 100,
"stone": 100,
"durable": 10000,
"defender": 1
},
{"type": 52,
"name": "土地Lv.2",
"level": 2,
"grain": 200,
"wood": 200,
"iron": 200,
"stone": 200,
"durable": 10000,
"defender": 2
},
{"type": 52,
"name": "土地Lv.3",
"level": 3,
"grain": 300,
"wood": 300,
"iron": 300,
"stone": 300,
"durable": 100,
"defender": 3
},
{"type": 52,
"name": "土地Lv.4",
"level": 4,
"grain": 0,
"wood": 4000,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 4
},
{"type": 52,
"name": "土地Lv.5",
"level": 5,
"grain": 0,
"wood": 5000,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 5
},
{"type": 52,
"name": "土地Lv.6",
"level": 6,
"grain": 0,
"wood": 1000,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 6
},
{"type": 52,
"name": "土地Lv.7",
"level": 7,
"grain": 0,
"wood": 2000,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 7
},
{"type": 52,
"name": "土地Lv.8",
"level": 8,
"grain": 0,
"wood": 3000,
"iron": 0,
"stone": 0,
"durable": 100,
"defender": 8
},
{"type": 53,
"name": "土地Lv.1",
"level": 1,
"grain": 100,
"wood": 100,
"iron": 100,
"stone": 100,
"durable": 10000,
"defender": 1
},
{"type": 53,
"name": "土地Lv.2",
"level": 2,
"grain": 200,
"wood": 200,
"iron": 200,
"stone": 200,
"durable": 10000,
"defender": 2
},
{"type": 53,
"name": "土地Lv.3",
"level": 3,
"grain": 300,
"wood": 300,
"iron": 300,
"stone": 300,
"durable": 100,
"defender": 3
},
{"type": 53,
"name": "土地Lv.4",
"level": 4,
"grain": 0,
"wood": 0,
"iron": 4000,
"stone": 0,
"durable": 10000,
"defender": 4
},
{"type": 53,
"name": "土地Lv.5",
"level": 5,
"grain": 0,
"wood": 0,
"iron": 5000,
"stone": 0,
"durable": 100,
"defender": 5
},
{"type": 53,
"name": "土地Lv.6",
"level": 6,
"grain": 0,
"wood": 0,
"iron": 6000,
"stone": 0,
"durable": 10000,
"defender": 6
},
{"type": 53,
"name": "土地Lv.7",
"level": 7,
"grain": 0,
"wood": 0,
"iron": 7000,
"stone": 0,
"durable": 10000,
"defender": 7
},
{"type": 53,
"name": "土地Lv.8",
"level": 8,
"grain": 0,
"wood": 0,
"iron": 8000,
"stone": 0,
"durable": 10000,
"defender": 8
},
{"type": 54,
"name": "土地Lv.1",
"level": 1,
"grain": 100,
"wood": 100,
"iron": 100,
"stone": 100,
"durable": 10000,
"defender": 1
},
{"type": 54,
"name": "土地Lv.2",
"level": 2,
"grain": 200,
"wood": 200,
"iron": 200,
"stone": 200,
"durable": 10000,
"defender": 2
},
{"type": 54,
"name": "土地Lv.3",
"level": 3,
"grain": 300,
"wood": 300,
"iron": 300,
"stone": 300,
"durable": 100,
"defender": 3
},
{"type": 54,
"name": "土地Lv.4",
"level": 4,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 1000,
"durable": 10000,
"defender": 1
},
{"type": 54,
"name": "土地Lv.5",
"level": 5,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 5000,
"durable": 10000,
"defender": 5
},
{"type": 54,
"name": "土地Lv.6",
"level": 6,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 6000,
"durable": 10000,
"defender": 6
},
{"type": 54,
"name": "土地Lv.7",
"level": 7,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 7000,
"durable": 10000,
"defender": 7
},
{"type": 54,
"name": "土地Lv.8",
"level": 8,
"grain": 0,
"wood": 0,
"iron": 0,
"stone": 8000,
"durable": 10000,
"defender": 8
},
{"type": 55,
"name": "土地Lv.1",
"level": 1,
"grain": 100,
"wood": 100,
"iron": 100,
"stone": 100,
"durable": 10000,
"defender": 1
},
{"type": 55,
"name": "土地Lv.2",
"level": 2,
"grain": 200,
"wood": 200,
"iron": 200,
"stone": 200,
"durable": 10000,
"defender": 2
},
{"type": 55,
"name": "土地Lv.3",
"level": 3,
"grain": 300,
"wood": 300,
"iron": 300,
"stone": 300,
"durable": 100,
"defender": 3
},
{"type": 55,
"name": "土地Lv.4",
"level": 4,
"grain": 1000,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 4
},
{"type": 55,
"name": "土地Lv.5",
"level": 5,
"grain": 5000,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 5
},
{"type": 55,
"name": "土地Lv.6",
"level": 6,
"grain": 6000,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 6
},
{"type": 55,
"name": "土地Lv.7",
"level": 7,
"grain": 7000,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 7
},
{"type": 55,
"name": "土地Lv.8",
"level": 8,
"grain": 8000,
"wood": 0,
"iron": 0,
"stone": 0,
"durable": 10000,
"defender": 8
}
]
}

View File

@@ -0,0 +1,43 @@
{
"title": "地图用户建设的建筑配置",
"cfg": [
{
"type": 56,
"name": "要塞",
"levels": [
{
"level": 1,
"durable": 10000,
"defender": 5,
"time": 20,
"need": {
"decree": 2,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"result": {
"army_cnt": 1
}
},
{
"level": 2,
"durable": 10000,
"defender": 5,
"time": 20,
"need": {
"decree": 3,
"grain": 1000,
"wood": 1000,
"iron": 1000,
"stone": 1000
},
"result": {
"army_cnt": 2
}
}
]
}
]
}

View File

@@ -0,0 +1,75 @@
{
"cfgId": 201,
"name": "百战精兵",
"trigger": 2,
"target": 2,
"des": "使自身攻击属性提升%n%、防御属性提升%n%、谋略属性提升%n%、速度属性提升%n%",
"limit": 3,
"arms": [
1,
4,
7,
2,
5,
8,
3,
6,
9
],
"include_effect": [
2,3,4,5
],
"levels": [
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50,50,50,50],
"effect_round": [0]
}
]
}

View File

@@ -0,0 +1,90 @@
{
"trigger_type": {
"des": "触发类型",
"list": [
{
"type": 1,
"des": "主动"
},
{
"type": 2,
"des": "被动"
},
{
"type": 3,
"des": "追击"
},
{
"type": 4,
"des": "指挥"
}
]
},
"effect_type": {
"des": "效果类型",
"list": [
{
"type": 1,
"des": "伤害率",
"isRate": true
},
{
"type": 2,
"des": "攻击",
"isRate": false
},
{
"type": 3,
"des": "防御",
"isRate": false
},
{
"type": 4,
"des": "谋略",
"isRate": false
},
{
"type": 5,
"des": "速度",
"isRate": false
}
]
},
"target_type": {
"des": "作用目标类型",
"list": [
{
"type": 1,
"des": "自己"
},
{
"type": 2,
"des": "我军单体"
},
{
"type": 3,
"des": "我军1-2个目标"
},
{
"type": 4,
"des": "我军全体"
},
{
"type": 5,
"des": "敌军单体"
},
{
"type": 6,
"des": "敌军1-2个目标"
},
{
"type": 7,
"des": "敌军2-3个目标"
},
{
"type": 8,
"des": "敌军全体"
}
]
}
}

View File

@@ -0,0 +1,72 @@
{
"cfgId": 401,
"name": "锋矢",
"trigger": 4,
"target": 2,
"des": "战斗中,使我军全体进行普通攻击的伤害提升%n%",
"limit": 3,
"arms": [
2,
5,
8,
3,
6,
9
],
"include_effect": [
1
],
"levels": [
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
}
]
}

View File

@@ -0,0 +1,72 @@
{
"cfgId": 101,
"name": "突击",
"trigger": 1,
"target": 5,
"des": "对敌军单体发动一次攻击(伤害率`%n%%`)",
"limit": 3,
"arms": [
1,
4,
7,
3,
6,
9
],
"include_effect": [
1
],
"levels": [
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
}
]
}

View File

@@ -0,0 +1,72 @@
{
"cfgId": 301,
"name": "重斩",
"trigger": 3,
"target": 5,
"des": "普通攻击后,对攻击目标再次发动攻击(伤害率%n%%",
"limit": 3,
"arms": [
1,
4,
7,
3,
6,
9
],
"include_effect": [
1
],
"levels": [
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
},
{
"probability":30,
"effect_value": [50],
"effect_round": [0]
}
]
}

1
data/conf/mapRes_0.json Normal file

File diff suppressed because one or more lines are too long

155
db/conn.go Normal file
View File

@@ -0,0 +1,155 @@
package db
import (
"database/sql"
"errors"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log/slog"
"os"
"path/filepath"
"slgserver/config"
"time"
"xorm.io/xorm"
"xorm.io/xorm/log"
)
var MasterDB *xorm.Engine
var logFileHandle *os.File
var (
ConnectDBErr = errors.New("connect db error")
UseDBErr = errors.New("use db error")
)
// TestDB 测试数据库
func TestDB() error {
mysqlConfig, err := config.GetSection("mysql")
if err != nil {
slog.Error("get mysql config error", "error", err)
panic(err)
}
tmpDns := fmt.Sprintf("%s:%s@tcp(%s:%s)/?charset=%s&parseTime=True&loc=Local",
mysqlConfig["user"],
mysqlConfig["password"],
mysqlConfig["host"],
mysqlConfig["port"],
mysqlConfig["charset"])
egnine, err := xorm.NewEngine("mysql", tmpDns)
if err != nil {
slog.Error("new engine error", "error", err)
panic(err)
}
defer egnine.Close()
// 测试数据库连接是否 OK
if err = egnine.Ping(); err != nil {
slog.Error("ping db error", "error", err)
return ConnectDBErr
}
_, err = egnine.Exec("use " + mysqlConfig["dbname"])
if err != nil {
slog.Error("use db error", "error", err)
// 使用参数化查询避免SQL注入风险
createDBQuery := fmt.Sprintf("CREATE DATABASE `%s` DEFAULT CHARACTER SET %s", mysqlConfig["dbname"], mysqlConfig["charset"])
_, err = egnine.Exec(createDBQuery)
if err != nil {
slog.Error("create database error", "error", err)
return UseDBErr
}
slog.Info("create database successfully")
}
// 初始化 MasterDB
return Init()
}
func Init() error {
mysqlConfig, err := config.GetSection("mysql")
if err != nil {
slog.Error("get mysql config error", "error", err)
return err
}
// 启动时就打开数据库连接
if err = initEngine(mysqlConfig); err != nil {
slog.Error("mysql is not open", "error", err)
return err
}
return nil
}
func fillDns(mysqlConfig map[string]string) string {
return fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=True&loc=Local",
mysqlConfig["user"],
mysqlConfig["password"],
mysqlConfig["host"],
mysqlConfig["port"],
mysqlConfig["dbname"],
mysqlConfig["charset"])
}
func initEngine(mysqlConfig map[string]string) error {
var err error
dns := fillDns(mysqlConfig)
MasterDB, err = xorm.NewEngine("mysql", dns)
if err != nil {
return err
}
// 数据库连接池配置,根据实际负载调整
maxIdle := config.GetInt("mysql.max_idle", 10) // 默认值从2提升到10
maxConn := config.GetInt("mysql.max_conn", 100) // 默认值从10提升到100
MasterDB.SetMaxIdleConns(maxIdle)
MasterDB.SetMaxOpenConns(maxConn)
// 设置连接最大生存时间,避免长时间连接导致的问题
MasterDB.SetConnMaxLifetime(time.Hour)
showSQL := config.GetBool("xorm.show_sql", false)
logLevel := config.GetInt("xorm.log_level", 1)
logFile := config.GetPath("xorm.log_file", "")
if logFile != "" {
if logFileHandle != nil {
logFileHandle.Close()
}
// 确保日志文件目录存在
if err := os.MkdirAll(filepath.Dir(logFile), 0755); err != nil {
return fmt.Errorf("failed to create log directory: %w", err)
}
f, err := os.OpenFile(logFile, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
logFileHandle = f
MasterDB.SetLogger(log.NewSimpleLogger(f))
}
MasterDB.SetLogLevel(log.LogLevel(logLevel))
MasterDB.ShowSQL(showSQL)
return nil
}
func StdMasterDB() *sql.DB {
return MasterDB.DB().DB
}
func Close() {
if MasterDB != nil {
MasterDB.Close()
}
if logFileHandle != nil {
logFileHandle.Close()
}
}

60
go.mod Normal file
View File

@@ -0,0 +1,60 @@
module slgserver
go 1.25
require (
github.com/gin-gonic/gin v1.11.0
github.com/go-sql-driver/mysql v1.9.3
github.com/go-think/openssl v1.20.0
github.com/gorilla/websocket v1.5.3
github.com/mitchellh/mapstructure v1.5.0
github.com/spf13/viper v1.21.0
gopkg.in/ini.v1 v1.67.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
xorm.io/xorm v1.3.11
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.2 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.11 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.28.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.56.0 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
go.uber.org/mock v0.6.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.23.0 // indirect
golang.org/x/crypto v0.44.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
xorm.io/builder v0.3.13 // indirect
)

200
go.sum Normal file
View File

@@ -0,0 +1,200 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.11 h1:AQvxbp830wPhHTqc1u7nzoLT+ZFxGY7emj5DR5DYFik=
github.com/gabriel-vasile/mimetype v1.4.11/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-think/openssl v1.20.0 h1:awyPzo559r0eOePQ35jLwXVjY0hws/2et+9OHQCEUZc=
github.com/go-think/openssl v1.20.0/go.mod h1:Ug+hrHD5ldaCqpHVuSaP1z/HogGbEtXEbUJmiHai+II=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY=
github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE=
modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo=
xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.11 h1:i4tlVUASogb0ZZFJHA7dZqoRU2pUpUsutnNdaOlFyMI=
xorm.io/xorm v1.3.11/go.mod h1:cs0ePc8O4a0jD78cNvD+0VFwhqotTvLQZv372QsDw7Q=

46
log/log.go Normal file
View File

@@ -0,0 +1,46 @@
package log
import (
"io"
"log/slog"
"os"
"path"
"path/filepath"
"slgserver/config"
"strings"
"gopkg.in/natefinch/lumberjack.v2"
)
func init() {
fileDir := config.GetString("log.file_dir", "../log/")
maxSize := config.GetInt("log.max_size", 128)
maxBackups := config.GetInt("log.max_backups", 30)
maxAge := config.GetInt("log.max_age", 7)
compress := config.GetBool("log.compress", true)
sa := strings.Split(filepath.Base(os.Args[0]), ".")
fileName := sa[0] + ".log"
hook := &lumberjack.Logger{
Filename: path.Join(fileDir, fileName), // 日志文件路径
MaxSize: maxSize, // 每个日志文件保存的最大尺寸 单位M
MaxBackups: maxBackups, // 日志文件最多保存多少个备份
MaxAge: maxAge, // 文件最多保存多少天
Compress: compress, // 是否压缩
}
// 创建多写入器:同时写入标准输出和文件
multiWriter := io.MultiWriter(os.Stdout, hook)
// 创建 JSON handler包含调用者信息
opts := &slog.HandlerOptions{
Level: slog.LevelInfo,
AddSource: true, // 添加调用者信息
}
handler := slog.NewJSONHandler(multiWriter, opts)
logger := slog.New(handler)
// 使用 slog 标准方式设置默认 logger
slog.SetDefault(logger)
}

BIN
main/chatserver.exe Normal file

Binary file not shown.

28
main/chatserver.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
"log/slog"
"os"
"slgserver/config"
"slgserver/net"
"slgserver/server/chatserver"
)
func getChatServerAddr() string {
host := config.GetString("chatserver.host", "")
port := config.GetString("chatserver.port", "8002")
return host + ":" + port
}
func main() {
if wd, err := os.Getwd(); err == nil {
slog.Info("working directory", "dir", wd)
} else {
slog.Warn("get working directory failed", "error", err)
}
chatserver.Init()
needSecret := config.GetBool("chatserver.need_secret", false)
s := net.NewServer(getChatServerAddr(), needSecret)
s.Router(chatserver.MyRouter)
s.Start()
}

BIN
main/gateserver.exe Normal file

Binary file not shown.

30
main/gateserver.go Normal file
View File

@@ -0,0 +1,30 @@
package main
import (
"log/slog"
"os"
"slgserver/config"
"slgserver/net"
"slgserver/server/gateserver"
"slgserver/server/gateserver/controller"
)
func getGateServerAddr() string {
host := config.GetString("gateserver.host", "")
port := config.GetString("gateserver.port", "8004")
return host + ":" + port
}
func main() {
if wd, err := os.Getwd(); err == nil {
slog.Info("working directory", "dir", wd)
} else {
slog.Warn("get working directory failed", "error", err)
}
gateserver.Init()
needSecret := config.GetBool("gateserver.need_secret", false)
s := net.NewServer(getGateServerAddr(), needSecret)
s.Router(gateserver.MyRouter)
s.SetOnBeforeClose(controller.GHandle.OnServerConnClose)
s.Start()
}

BIN
main/httpserver.exe Normal file

Binary file not shown.

29
main/httpserver.go Normal file
View File

@@ -0,0 +1,29 @@
package main
import (
"github.com/gin-gonic/gin"
"slgserver/config"
"slgserver/db"
"slgserver/server/httpserver/controller"
)
func main() {
// 设置 Gin 为 release 模式
gin.SetMode(gin.ReleaseMode)
db.TestDB()
r := gin.Default()
g := r.Group("")
new(controller.AccountController).RegisterRoutes(g)
addr := getHttpAddr()
r.Run(addr)
}
func getHttpAddr() string {
host := config.GetString("httpserver.host", "")
port := config.GetString("httpserver.port", "8088")
return host + ":" + port
}

BIN
main/loginserver.exe Normal file

Binary file not shown.

28
main/loginserver.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
"log/slog"
"os"
"slgserver/config"
"slgserver/net"
"slgserver/server/loginserver"
)
func getLoginServerAddr() string {
host := config.GetString("loginserver.host", "")
port := config.GetString("loginserver.port", "8003")
return host + ":" + port
}
func main() {
if wd, err := os.Getwd(); err == nil {
slog.Info("working directory", "dir", wd)
} else {
slog.Warn("get working directory failed", "error", err)
}
loginserver.Init()
needSecret := config.GetBool("loginserver.need_secret", false)
s := net.NewServer(getLoginServerAddr(), needSecret)
s.Router(loginserver.MyRouter)
s.Start()
}

BIN
main/slgserver.exe Normal file

Binary file not shown.

28
main/slgserver.go Normal file
View File

@@ -0,0 +1,28 @@
package main
import (
"log/slog"
"os"
"slgserver/config"
"slgserver/net"
"slgserver/server/slgserver/run"
)
func getServerAddr() string {
host := config.GetString("slgserver.host", "")
port := config.GetString("slgserver.port", "8001")
return host + ":" + port
}
func main() {
if wd, err := os.Getwd(); err == nil {
slog.Info("working directory", "dir", wd)
} else {
slog.Warn("get working directory failed", "error", err)
}
run.Init()
needSecret := config.GetBool("slgserver.need_secret", false)
s := net.NewServer(getServerAddr(), needSecret)
s.Router(run.MyRouter)
s.Start()
}

125
main/wstest.go Normal file
View File

@@ -0,0 +1,125 @@
package main
import (
"github.com/go-think/openssl"
"github.com/mitchellh/mapstructure"
"github.com/gorilla/websocket"
"log/slog"
"slgserver/net"
proto2 "slgserver/server/loginserver/proto"
"slgserver/util"
"time"
)
var origin = "httpserver://127.0.0.1:8002/"
var secretKey = []byte("")
var session = ""
func main() {
var dialer *websocket.Dialer
//通过Dialer连接websocket服务器
conn, _, err := dialer.Dial("ws://127.0.0.1:8001", nil)
if err != nil {
slog.Error("dial websocket failed", "error", err)
return
}
go do(conn)
go timeWriter(conn)
time.Sleep(10 * time.Second)
}
func do(conn *websocket.Conn) {
defer func() {
if err := recover(); err != nil {
slog.Error("do panic", "error", err)
}
}()
for {
_, message, _ := conn.ReadMessage()
msg := &net.RspBody{}
if len(secretKey) == 0 {
message, _ = util.UnZip(message)
if err := util.Unmarshal(message, msg); err == nil {
if msg.Name == "handshake" {
h := &net.Handshake{}
mapstructure.Decode(msg.Msg, h)
secretKey = []byte(h.Key)
}
slog.Info("received message", "name", msg.Name)
}
} else {
message, _ = util.UnZip(message)
data, err := util.AesCBCDecrypt(message, secretKey, secretKey, openssl.ZEROS_PADDING)
if err == nil {
if err := util.Unmarshal(data, msg); err == nil {
slog.Info("received message",
"name", msg.Name,
"code", msg.Code,
"payload", msg.Msg)
if msg.Name == "login" {
lr := &proto2.LoginRsp{}
mapstructure.Decode(msg.Msg, lr)
session = lr.Session
}
} else {
secretKey = []byte("")
}
} else {
secretKey = []byte("")
}
}
}
}
func login(conn *websocket.Conn) {
l := &proto2.LoginReq{Ip: "127.0.0.1", Username: "test", Password: "123456"}
send(conn, "login", l)
}
func reLogin(conn *websocket.Conn, session string) {
l := &proto2.ReLoginReq{Session: session}
slog.Info("relogin session", "session", session)
send(conn, "reLogin", l)
}
func logout(conn *websocket.Conn) {
l := &proto2.LogoutReq{UId: 5}
send(conn, "logout", l)
}
func send(conn *websocket.Conn, name string, dd interface{}) {
msg := &net.ReqBody{Name: name, Msg: dd}
if len(secretKey) == 0 {
} else {
if data, err := util.Marshal(msg); err == nil {
data, _ := util.AesCBCEncrypt(data, secretKey, secretKey, openssl.ZEROS_PADDING)
data, _ = util.Zip(data)
conn.WriteMessage(websocket.BinaryMessage, data)
}
}
}
func timeWriter(conn *websocket.Conn) {
time.Sleep(time.Second * 1)
login(conn)
time.Sleep(time.Second * 1)
reLogin(conn, session)
time.Sleep(20 * time.Second)
//time.Sleep(time.Second * 1)
//reLogin(conn, "123")
//
//time.Sleep(time.Second * 1)
//logout(conn)
}

25
middleware/check_login.go Normal file
View File

@@ -0,0 +1,25 @@
package middleware
import (
"log/slog"
"slgserver/constant"
"slgserver/net"
)
func CheckLogin() net.MiddlewareFunc {
return func(next net.HandlerFunc) net.HandlerFunc {
return func(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
_, err := req.Conn.GetProperty("uid")
if err != nil {
slog.Warn("connect not found uid",
"msgName", req.Body.Name)
rsp.Body.Code = constant.UserNotInConnect
req.Conn.Push("account.pleaseLogin", nil)
return
}
next(req, rsp)
}
}
}

23
middleware/check_rid.go Normal file
View File

@@ -0,0 +1,23 @@
package middleware
import (
"log/slog"
"slgserver/constant"
"slgserver/net"
)
func CheckRId() net.MiddlewareFunc {
return func(next net.HandlerFunc) net.HandlerFunc {
return func(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
_, err := req.Conn.GetProperty("rid")
if err != nil {
rsp.Body.Code = constant.RoleNotInConnect
slog.Warn("connect not found role",
"msgName", req.Body.Name)
return
}
next(req, rsp)
}
}
}

23
middleware/check_role.go Normal file
View File

@@ -0,0 +1,23 @@
package middleware
import (
"log/slog"
"slgserver/constant"
"slgserver/net"
)
func CheckRole() net.MiddlewareFunc {
return func(next net.HandlerFunc) net.HandlerFunc {
return func(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
_, err := req.Conn.GetProperty("role")
if err != nil {
rsp.Body.Code = constant.RoleNotInConnect
slog.Warn("connect not found role",
"msgName", req.Body.Name)
return
}
next(req, rsp)
}
}
}

View File

@@ -0,0 +1,23 @@
package middleware
import (
"fmt"
"log/slog"
"slgserver/net"
"time"
)
func ElapsedTime() net.MiddlewareFunc {
return func(next net.HandlerFunc) net.HandlerFunc {
return func(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
bt := time.Now().UnixNano()
next(req, rsp)
et := time.Now().UnixNano()
diff := (et - bt)/int64(time.Millisecond)
slog.Info("ElapsedTime:",
"msgName", req.Body.Name,
"cost", fmt.Sprintf("%dms", diff))
}
}
}

20
middleware/log.go Normal file
View File

@@ -0,0 +1,20 @@
package middleware
import (
"fmt"
"log/slog"
"slgserver/net"
)
func Log() net.MiddlewareFunc {
return func(next net.HandlerFunc) net.HandlerFunc {
return func(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
slog.Info("client req",
"msgName", req.Body.Name,
"data", fmt.Sprintf("%v", req.Body.Msg))
next(req, rsp)
}
}
}

254
net/clientconn.go Normal file
View File

@@ -0,0 +1,254 @@
package net
import (
"context"
"errors"
"fmt"
"github.com/go-think/openssl"
"github.com/mitchellh/mapstructure"
"github.com/gorilla/websocket"
"log/slog"
"slgserver/constant"
"slgserver/util"
"sync"
"time"
)
// 客户端连接
type ClientConn struct {
wsSocket *websocket.Conn // 底层websocket
isClosed bool
Seq int64
onClose func(conn*ClientConn)
onPush func(conn*ClientConn, body*RspBody)
//链接属性
property map[string]interface{}
//保护链接属性修改的锁
propertyLock sync.RWMutex
syncCtxs map[int64]*syncCtx
syncLock sync.RWMutex
handshakeChan chan bool
handshake bool
}
func NewClientConn(wsSocket *websocket.Conn) *ClientConn {
conn := &ClientConn{
wsSocket: wsSocket,
isClosed: false,
property: make(map[string]interface{}),
Seq: 0,
syncCtxs: make(map[int64]*syncCtx),
handshakeChan: make(chan bool),
}
return conn
}
func (this *ClientConn) waitHandshake() bool{
if this.handshake == false{
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case _ = <-this.handshakeChan:{
slog.Info("recv handshakeChan")
return true
}
case <-ctx.Done():{
slog.Info("recv handshakeChan timeout")
return false
}
}
}
return true
}
func (this *ClientConn) Start() bool{
this.handshake = false
go this.wsReadLoop()
return this.waitHandshake()
}
func (this *ClientConn) Addr() string {
return this.wsSocket.RemoteAddr().String()
}
func (this *ClientConn) Push(name string, data interface{}) {
rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: 0}}
this.write(rsp.Body)
}
func (this *ClientConn) Send(name string, data interface{}) *RspBody{
this.syncLock.Lock()
sync := newSyncCtx()
this.Seq += 1
seq := this.Seq
req := ReqBody{Name: name, Msg: data, Seq: seq}
this.syncCtxs[this.Seq] = sync
this.syncLock.Unlock()
rsp := &RspBody{Code: constant.OK, Name: name, Seq: seq }
err := this.write(req)
if err != nil{
sync.cancel()
}else{
r := sync.wait()
if r == nil{
rsp.Code = constant.ProxyConnectError
}else{
rsp = r
}
}
this.syncLock.Lock()
delete(this.syncCtxs, seq)
this.syncLock.Unlock()
return rsp
}
func (this *ClientConn) wsReadLoop() {
defer func() {
if err := recover(); err != nil {
e := fmt.Sprintf("%v", err)
slog.Error("wsReadLoop error", "err", e)
this.Close()
}
}()
for {
// 读一个message
_, data, err := this.wsSocket.ReadMessage()
if err != nil {
break
}
data, err = util.UnZip(data)
if err != nil {
slog.Error("wsReadLoop UnZip error", "error", err)
continue
}
//需要检测是否有加密
body := &RspBody{}
if secretKey, err := this.GetProperty("secretKey"); err == nil {
key := secretKey.(string)
d, err := util.AesCBCDecrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
if err != nil {
slog.Error("AesDecrypt error", "error", err)
}else{
data = d
}
}
if err := util.Unmarshal(data, body); err == nil {
if body.Seq == 0 {
if body.Name == HandshakeMsg{
h := Handshake{}
mapstructure.Decode(body.Msg, &h)
slog.Info("client 收到握手协议", "data", string(data))
if h.Key != ""{
this.SetProperty("secretKey", h.Key)
}else{
this.RemoveProperty("secretKey")
}
this.handshake = true
this.handshakeChan <- true
}else{
//推送,需要推送到指定的代理连接
if this.onPush != nil{
this.onPush(this, body)
}else{
slog.Warn("clientconn not deal push")
}
}
}else{
this.syncLock.RLock()
s, ok := this.syncCtxs[body.Seq]
this.syncLock.RUnlock()
if ok {
s.outChan <- body
}else{
slog.Warn("seq not found sync",
"seq", body.Seq,
"msgName", body.Name)
}
}
}else{
slog.Error("wsReadLoop Unmarshal error", "error", err)
}
}
this.Close()
}
func (this *ClientConn) write(msg interface{}) error{
data, err := util.Marshal(msg)
if err == nil {
if secretKey, err:= this.GetProperty("secretKey"); err == nil {
key := secretKey.(string)
slog.Info("secretKey", "secretKey", key)
data, _ = util.AesCBCEncrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
}
}else {
slog.Error("wsWriteLoop Marshal body error", "error", err)
return err
}
if data, err := util.Zip(data); err == nil{
if err := this.wsSocket.WriteMessage(websocket.BinaryMessage, data); err != nil {
this.Close()
return err
}
}else{
return err
}
return nil
}
func (this *ClientConn) Close() {
this.wsSocket.Close()
if !this.isClosed {
this.isClosed = true
if this.onClose != nil{
this.onClose(this)
}
}
}
//设置链接属性
func (this *ClientConn) SetProperty(key string, value interface{}) {
this.propertyLock.Lock()
defer this.propertyLock.Unlock()
this.property[key] = value
}
//获取链接属性
func (this *ClientConn) GetProperty(key string) (interface{}, error) {
this.propertyLock.RLock()
defer this.propertyLock.RUnlock()
if value, ok := this.property[key]; ok {
return value, nil
} else {
return nil, errors.New("no property found")
}
}
//移除链接属性
func (this *ClientConn) RemoveProperty(key string) {
this.propertyLock.Lock()
defer this.propertyLock.Unlock()
delete(this.property, key)
}
func (this *ClientConn) SetOnClose(hookFunc func (*ClientConn)) {
this.onClose = hookFunc
}
func (this *ClientConn) SetOnPush(hookFunc func (*ClientConn, *RspBody)) {
this.onPush = hookFunc
}

70
net/conn.go Normal file
View File

@@ -0,0 +1,70 @@
package net
import (
"context"
"time"
)
type ReqBody struct {
Seq int64 `json:"seq"`
Name string `json:"name"`
Msg interface{} `json:"msg"`
Proxy string `json:"proxy"`
}
type RspBody struct {
Seq int64 `json:"seq"`
Name string `json:"name"`
Code int `json:"code"`
Msg interface{} `json:"msg"`
}
type WsMsgReq struct {
Body *ReqBody
Conn WSConn
}
type WsMsgRsp struct {
Body* RspBody
}
const HandshakeMsg = "handshake"
const HeartbeatMsg = "heartbeat"
type Handshake struct {
Key string `json:"key"`
}
type Heartbeat struct {
CTime int64 `json:"ctime"`
STime int64 `json:"stime"`
}
type WSConn interface {
SetProperty(key string, value interface{})
GetProperty(key string) (interface{}, error)
RemoveProperty(key string)
Addr() string
Push(name string, data interface{})
}
type syncCtx struct {
ctx context.Context
cancel context.CancelFunc
outChan chan *RspBody
}
func newSyncCtx() *syncCtx {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
return &syncCtx{ctx: ctx, cancel: cancel, outChan: make(chan *RspBody)}
}
func (this* syncCtx) wait() *RspBody{
defer this.cancel()
select {
case data := <- this.outChan:
return data
case <-this.ctx.Done():
return nil
}
}

202
net/connMgr.go Normal file
View File

@@ -0,0 +1,202 @@
package net
import (
"github.com/gorilla/websocket"
"log/slog"
"slgserver/server/slgserver/conn"
"slgserver/server/slgserver/pos"
"sync"
"sync/atomic"
)
var ConnMgr = Mgr{}
var cid int64 = 0
type Mgr struct {
cm sync.RWMutex
um sync.RWMutex
rm sync.RWMutex
connCache map[int64]WSConn
userCache map[int]WSConn
roleCache map[int]WSConn
}
func (this *Mgr) NewConn(wsSocket *websocket.Conn, needSecret bool) *ServerConn {
this.cm.Lock()
defer this.cm.Unlock()
id := atomic.AddInt64(&cid, 1)
if this.connCache == nil {
this.connCache = make(map[int64]WSConn)
}
if this.userCache == nil {
this.userCache = make(map[int]WSConn)
}
if this.roleCache == nil {
this.roleCache = make(map[int]WSConn)
}
c := NewServerConn(wsSocket, needSecret)
c.SetProperty("cid", id)
this.connCache[id] = c
return c
}
func (this *Mgr) UserLogin(conn WSConn, session string, uid int) {
this.um.Lock()
defer this.um.Unlock()
oldConn, ok := this.userCache[uid]
if ok {
if conn != oldConn {
slog.Warn("rob login",
"uid", uid,
"oldAddr", oldConn.Addr(),
"newAddr", conn.Addr())
//这里需要通知旧端被抢登录
oldConn.Push("robLogin", nil)
}
}
this.userCache[uid] = conn
conn.SetProperty("session", session)
conn.SetProperty("uid", uid)
}
func (this *Mgr) UserLogout(conn WSConn) {
this.removeUser(conn)
}
func (this *Mgr) removeUser(conn WSConn) {
this.um.Lock()
uid, err := conn.GetProperty("uid")
if err == nil {
//只删除自己的conn
id := uid.(int)
c, ok := this.userCache[id]
if ok && c == conn {
delete(this.userCache, id)
}
}
this.um.Unlock()
this.rm.Lock()
rid, err := conn.GetProperty("rid")
if err == nil {
//只删除自己的conn
id := rid.(int)
c, ok := this.roleCache[id]
if ok && c == conn {
delete(this.roleCache, id)
}
}
this.rm.Unlock()
conn.RemoveProperty("session")
conn.RemoveProperty("uid")
conn.RemoveProperty("role")
conn.RemoveProperty("rid")
}
func (this *Mgr) RoleEnter(conn WSConn, rid int) {
this.rm.Lock()
defer this.rm.Unlock()
conn.SetProperty("rid", rid)
this.roleCache[rid] = conn
}
func (this *Mgr) RemoveConn(conn WSConn) {
this.cm.Lock()
cid, err := conn.GetProperty("cid")
if err == nil {
delete(this.connCache, cid.(int64))
conn.RemoveProperty("cid")
}
this.cm.Unlock()
this.removeUser(conn)
}
func (this *Mgr) PushByRoleId(rid int, msgName string, data interface{}) bool {
if rid <= 0 {
return false
}
this.rm.Lock()
defer this.rm.Unlock()
conn, ok := this.roleCache[rid]
if ok {
conn.Push(msgName, data)
return true
} else {
return false
}
}
func (this *Mgr) Count() int {
this.cm.RLock()
defer this.cm.RUnlock()
return len(this.connCache)
}
func (this *Mgr) Push(pushSync conn.PushSync) {
proto := pushSync.ToProto()
belongRIds := pushSync.BelongToRId()
isCellView := pushSync.IsCellView()
x, y := pushSync.Position()
cells := make(map[int]int)
//推送给开始位置
if isCellView {
cellRIds := pos.RPMgr.GetCellRoleIds(x, y, 8, 6)
for _, rid := range cellRIds {
//是否能出现在视野
if can := pushSync.IsCanView(rid, x, y); can {
this.PushByRoleId(rid, pushSync.PushMsgName(), proto)
cells[rid] = rid
}
}
}
//推送给目标位置
tx, ty := pushSync.TPosition()
if tx >= 0 && ty >= 0 {
var cellRIds []int
if isCellView {
cellRIds = pos.RPMgr.GetCellRoleIds(tx, ty, 8, 6)
} else {
cellRIds = pos.RPMgr.GetCellRoleIds(tx, ty, 0, 0)
}
for _, rid := range cellRIds {
if _, ok := cells[rid]; !ok {
if can := pushSync.IsCanView(rid, tx, ty); can {
this.PushByRoleId(rid, pushSync.PushMsgName(), proto)
cells[rid] = rid
}
}
}
}
//推送给自己
for _, rid := range belongRIds {
if _, ok := cells[rid]; !ok {
this.PushByRoleId(rid, pushSync.PushMsgName(), proto)
}
}
}
func (this *Mgr) pushAll(msgName string, data interface{}) {
this.rm.Lock()
defer this.rm.Unlock()
for _, conn := range this.roleCache {
conn.Push(msgName, data)
}
}

68
net/proxyClient.go Normal file
View File

@@ -0,0 +1,68 @@
package net
import (
"errors"
"github.com/gorilla/websocket"
"time"
)
type ProxyClient struct {
proxy string
conn *ClientConn
}
func (this*ProxyClient) Connect() error {
var dialer = websocket.Dialer{
Subprotocols: []string{"p1", "p2"},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
HandshakeTimeout: 30 * time.Second,
}
ws, _, err := dialer.Dial(this.proxy, nil)
if err == nil{
this.conn = NewClientConn(ws)
if this.conn.Start() == false{
return errors.New("handshake fail")
}
}
return err
}
func (this*ProxyClient) Send(msgName string, msg interface{}) (*RspBody, error){
if this.conn != nil {
return this.conn.Send(msgName, msg), nil
}
return nil, errors.New("conn not found")
}
func (this *ProxyClient) SetOnPush(hookFunc func (*ClientConn, *RspBody)) {
if this.conn != nil {
this.conn.SetOnPush(hookFunc)
}
}
func (this *ProxyClient) SetOnClose(hookFunc func (*ClientConn)) {
if this.conn != nil {
this.conn.SetOnClose(hookFunc)
}
}
func (this *ProxyClient) SetProperty(key string, value interface{}) {
if this.conn != nil {
this.conn.SetProperty(key, value)
}
}
func (this *ProxyClient) Close() {
if this.conn != nil {
this.conn.Close()
}
}
func NewProxyClient(proxy string) *ProxyClient {
return & ProxyClient{
proxy: proxy,
}
}

95
net/router.go Normal file
View File

@@ -0,0 +1,95 @@
package net
import (
"encoding/json"
"log/slog"
"strings"
)
type HandlerFunc func(req *WsMsgReq, rsp *WsMsgRsp)
type MiddlewareFunc func(HandlerFunc) HandlerFunc
type Group struct {
prefix string
hMap map[string]HandlerFunc
hMapMidd map[string][]MiddlewareFunc
middleware []MiddlewareFunc
}
func (this *Group) AddRouter(name string, handlerFunc HandlerFunc, middleware ...MiddlewareFunc) {
this.hMap[name] = handlerFunc
this.hMapMidd[name] = middleware
}
func (this *Group) Use(middleware ...MiddlewareFunc) *Group {
this.middleware = append(this.middleware, middleware...)
return this
}
func (this *Group) applyMiddleware(name string) HandlerFunc {
h, ok := this.hMap[name]
if !ok {
//通配符
h, ok = this.hMap["*"]
}
if ok {
for i := len(this.middleware) - 1; i >= 0; i-- {
h = this.middleware[i](h)
}
for i := len(this.hMapMidd[name]) - 1; i >= 0; i-- {
h = this.hMapMidd[name][i](h)
}
}
return h
}
func (this *Group) exec(name string, req *WsMsgReq, rsp *WsMsgRsp) {
slog.Debug("route exec", "group", this.prefix, "name", name)
h := this.applyMiddleware(name)
if h == nil {
slog.Warn("Group has not",
"msgName", req.Body.Name)
} else {
h(req, rsp)
}
if dd, err := json.Marshal(rsp.Body); err == nil {
slog.Debug("route response", "group", this.prefix, "name", name, "size", len(dd))
}
}
type Router struct {
groups []*Group
}
func (this *Router) Group(prefix string) *Group {
g := &Group{prefix: prefix,
hMap: make(map[string]HandlerFunc),
hMapMidd: make(map[string][]MiddlewareFunc),
}
this.groups = append(this.groups, g)
return g
}
func (this *Router) Run(req *WsMsgReq, rsp *WsMsgRsp) {
name := req.Body.Name
msgName := name
sArr := strings.Split(name, ".")
prefix := ""
if len(sArr) == 2 {
prefix = sArr[0]
msgName = sArr[1]
}
for _, g := range this.groups {
if g.prefix == prefix {
g.exec(msgName, req, rsp)
} else if g.prefix == "*" {
g.exec(msgName, req, rsp)
}
}
}

137
net/server.go Normal file
View File

@@ -0,0 +1,137 @@
package net
import (
"context"
"github.com/gorilla/websocket"
"log/slog"
"net/http"
"os"
"os/signal"
"slgserver/config"
"strings"
"syscall"
"time"
)
// http升级websocket协议的配置
var wsUpgrader = websocket.Upgrader{
// 允许配置的 CORS 请求
CheckOrigin: checkOrigin,
}
var allowedOrigins = loadAllowedOrigins()
type server struct {
addr string
router *Router
needSecret bool
beforeClose func(WSConn)
httpServer *http.Server
}
func NewServer(addr string, needSecret bool) *server {
s := server{
addr: addr,
needSecret: needSecret,
}
return &s
}
func (this *server) Router(router *Router) {
this.router = router
}
func (this *server) Start() {
mux := http.NewServeMux()
mux.HandleFunc("/", this.wsHandler)
this.httpServer = &http.Server{
Addr: this.addr,
Handler: mux,
}
slog.Info("server starting", "addr", this.addr)
go func() {
if err := this.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
slog.Error("server start failed", "error", err, "addr", this.addr)
}
}()
this.waitForShutdown()
}
func (this *server) waitForShutdown() {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
slog.Info("server shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := this.httpServer.Shutdown(ctx); err != nil {
slog.Error("server shutdown error", "error", err)
} else {
slog.Info("server shutdown gracefully")
}
}
func (this *server) SetOnBeforeClose(hookFunc func(WSConn)) {
this.beforeClose = hookFunc
}
func (this *server) wsHandler(resp http.ResponseWriter, req *http.Request) {
wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
if err != nil {
slog.Warn("websocket upgrade failed", "error", err, "remote", req.RemoteAddr)
return
}
conn := ConnMgr.NewConn(wsSocket, this.needSecret)
slog.Info("client connect", "addr", wsSocket.RemoteAddr().String())
conn.SetRouter(this.router)
conn.SetOnClose(ConnMgr.RemoveConn)
conn.SetOnBeforeClose(this.beforeClose)
conn.Start()
conn.Handshake()
}
func checkOrigin(r *http.Request) bool {
if len(allowedOrigins) == 0 {
return true
}
origin := strings.ToLower(strings.TrimSpace(r.Header.Get("Origin")))
if origin == "" {
return true
}
if _, ok := allowedOrigins[origin]; ok {
return true
}
slog.Warn("origin not allowed", "origin", origin)
return false
}
func loadAllowedOrigins() map[string]struct{} {
origins := config.GetString("server.allowed_origins", "")
if origins == "" {
return nil
}
originMap := make(map[string]struct{})
items := strings.Split(origins, ",")
for _, item := range items {
val := strings.ToLower(strings.TrimSpace(item))
if val == "" {
continue
}
if val == "*" {
return nil
}
originMap[val] = struct{}{}
}
return originMap
}

287
net/serverconn.go Normal file
View File

@@ -0,0 +1,287 @@
package net
import (
"errors"
"fmt"
"log/slog"
"slgserver/util"
"sync"
"time"
"github.com/go-think/openssl"
"github.com/mitchellh/mapstructure"
"github.com/gorilla/websocket"
)
// 客户端连接
type ServerConn struct {
wsSocket *websocket.Conn // 底层websocket
outChan chan *WsMsgRsp // 写队列
isClosed bool
needSecret bool
Seq int64
router *Router
beforeClose func(conn WSConn)
onClose func(conn WSConn)
//链接属性
property map[string]interface{}
//保护链接属性修改的锁
propertyLock sync.RWMutex
closeChan chan struct{}
closeOnce sync.Once
}
func NewServerConn(wsSocket *websocket.Conn, needSecret bool) *ServerConn {
conn := &ServerConn{
wsSocket: wsSocket,
outChan: make(chan *WsMsgRsp, 1000),
isClosed: false,
property: make(map[string]interface{}),
needSecret: needSecret,
Seq: 0,
closeChan: make(chan struct{}),
}
return conn
}
// 开启异步
func (this *ServerConn) Start() {
go this.wsReadLoop()
go this.wsWriteLoop()
}
func (this *ServerConn) Addr() string {
return this.wsSocket.RemoteAddr().String()
}
func (this *ServerConn) Push(name string, data interface{}) {
rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: 0}}
this.enqueue(rsp)
}
func (this *ServerConn) Send(name string, data interface{}) {
this.Seq += 1
rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: this.Seq}}
this.enqueue(rsp)
}
func (this *ServerConn) wsReadLoop() {
defer func() {
if err := recover(); err != nil {
e := fmt.Sprintf("%v", err)
slog.Error("wsReadLoop error", "err", e)
this.Close()
}
}()
for {
select {
case <-this.closeChan:
return
default:
}
// 读一个message
_, data, err := this.wsSocket.ReadMessage()
slog.Debug("ws read message", "size", len(data))
if err != nil {
break
}
data, err = util.UnZip(data)
if err != nil {
slog.Error("wsReadLoop UnZip error", "error", err)
continue
}
body := &ReqBody{}
if this.needSecret {
//检测是否有加密没有加密发起Handshake
if secretKey, err := this.GetProperty("secretKey"); err == nil {
key := secretKey.(string)
d, err := util.AesCBCDecrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
if err != nil {
slog.Error("AesDecrypt error", "error", err)
this.Handshake()
} else {
data = d
}
} else {
slog.Info("secretKey not found client need handshake", "error", err)
this.Handshake()
return
}
}
if err := util.Unmarshal(data, body); err == nil {
req := &WsMsgReq{Conn: this, Body: body}
rsp := &WsMsgRsp{Body: &RspBody{Name: body.Name, Seq: req.Body.Seq}}
if req.Body.Name == HeartbeatMsg {
h := &Heartbeat{}
mapstructure.Decode(body.Msg, h)
h.STime = time.Now().UnixNano() / 1e6
rsp.Body.Msg = h
} else {
if this.router != nil {
this.router.Run(req, rsp)
}
}
this.outChan <- rsp
} else {
slog.Error("wsReadLoop Unmarshal error", "error", err)
this.Handshake()
}
}
this.Close()
}
func (this *ServerConn) wsWriteLoop() {
defer func() {
if err := recover(); err != nil {
slog.Error("wsWriteLoop error")
this.Close()
}
}()
for {
select {
case <-this.closeChan:
return
case msg := <-this.outChan:
if msg == nil {
continue
}
// 写给websocket
this.write(msg.Body)
}
}
}
func (this *ServerConn) write(msg interface{}) error {
data, err := util.Marshal(msg)
slog.Debug("ws write message", "size", len(data))
if err == nil {
if this.needSecret {
if secretKey, err := this.GetProperty("secretKey"); err == nil {
key := secretKey.(string)
data, _ = util.AesCBCEncrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
}
}
} else {
slog.Error("wsWriteLoop Marshal body error", "error", err)
return err
}
if data, err := util.Zip(data); err == nil {
if err := this.wsSocket.WriteMessage(websocket.BinaryMessage, data); err != nil {
this.Close()
return err
}
} else {
return err
}
return nil
}
func (this *ServerConn) Close() {
this.closeOnce.Do(func() {
this.wsSocket.Close()
close(this.closeChan)
if !this.isClosed {
this.isClosed = true
if this.beforeClose != nil {
this.beforeClose(this)
}
if this.onClose != nil {
this.onClose(this)
}
}
})
}
// 设置链接属性
func (this *ServerConn) SetProperty(key string, value interface{}) {
this.propertyLock.Lock()
defer this.propertyLock.Unlock()
this.property[key] = value
}
// 获取链接属性
func (this *ServerConn) GetProperty(key string) (interface{}, error) {
this.propertyLock.RLock()
defer this.propertyLock.RUnlock()
if value, ok := this.property[key]; ok {
return value, nil
} else {
return nil, errors.New("no property found")
}
}
func (this *ServerConn) SetRouter(router *Router) {
this.router = router
}
func (this *ServerConn) SetOnClose(hookFunc func(WSConn)) {
this.onClose = hookFunc
}
func (this *ServerConn) SetOnBeforeClose(hookFunc func(WSConn)) {
this.beforeClose = hookFunc
}
// 移除链接属性
func (this *ServerConn) RemoveProperty(key string) {
this.propertyLock.Lock()
defer this.propertyLock.Unlock()
delete(this.property, key)
}
// 握手协议
func (this *ServerConn) Handshake() {
secretKey := ""
if this.needSecret {
key, err := this.GetProperty("secretKey")
if err == nil {
secretKey = key.(string)
} else {
secretKey = util.RandSeq(16)
}
}
handshake := &Handshake{Key: secretKey}
body := &RspBody{Name: HandshakeMsg, Msg: handshake}
if data, err := util.Marshal(body); err == nil {
if secretKey != "" {
this.SetProperty("secretKey", secretKey)
} else {
this.RemoveProperty("secretKey")
}
slog.Info("handshake secretKey",
"secretKey", secretKey)
if data, err = util.Zip(data); err == nil {
this.wsSocket.WriteMessage(websocket.BinaryMessage, data)
}
} else {
slog.Error("handshake Marshal body error", "error", err)
}
}
func (this *ServerConn) enqueue(rsp *WsMsgRsp) {
select {
case <-this.closeChan:
return
case this.outChan <- rsp:
}
}

View File

@@ -0,0 +1,204 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"log/slog"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/chatserver/logic"
"slgserver/server/chatserver/proto"
"slgserver/util"
"sync"
)
var DefaultChat = Chat{
worldGroup: logic.NewGroup(),
unionGroups: make(map[int]*logic.Group),
ridToUnionGroups: make(map[int]int),
}
type Chat struct {
unionMutex sync.RWMutex
worldGroup *logic.Group //世界频道
unionGroups map[int]*logic.Group //联盟频道
ridToUnionGroups map[int]int //rid对应的联盟频道
}
func (this*Chat) InitRouter(r *net.Router) {
g := r.Group("chat").Use(middleware.ElapsedTime(), middleware.Log())
g.AddRouter("login", this.login)
g.AddRouter("logout", this.logout, middleware.CheckRId())
g.AddRouter("chat", this.chat, middleware.CheckRId())
g.AddRouter("history", this.history, middleware.CheckRId())
g.AddRouter("join", this.join, middleware.CheckRId())
g.AddRouter("exit", this.exit, middleware.CheckRId())
}
func (this*Chat) login(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.LoginReq{}
rspObj := &proto.LoginRsp{}
rsp.Body.Code = constant.OK
rsp.Body.Msg = rspObj
mapstructure.Decode(req.Body.Msg, reqObj)
rspObj.RId = reqObj.RId
rspObj.NickName = reqObj.NickName
sess, err := util.ParseSession(reqObj.Token)
if err != nil{
rsp.Body.Code = constant.InvalidParam
return
}
if sess.IsValid() == false || sess.Id != reqObj.RId{
rsp.Body.Code = constant.InvalidParam
return
}
net.ConnMgr.RoleEnter(req.Conn, reqObj.RId)
this.worldGroup.Enter(logic.NewUser(reqObj.RId, reqObj.NickName))
}
func (this*Chat) logout(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.LogoutReq{}
rspObj := &proto.LogoutRsp{}
rsp.Body.Code = constant.OK
rsp.Body.Msg = rspObj
mapstructure.Decode(req.Body.Msg, reqObj)
rspObj.RId = reqObj.RId
net.ConnMgr.UserLogout(req.Conn)
this.worldGroup.Exit(reqObj.RId)
}
func (this*Chat) chat(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ChatReq{}
rspObj := &proto.ChatMsg{}
rsp.Body.Code = constant.OK
rsp.Body.Msg = rspObj
mapstructure.Decode(req.Body.Msg, reqObj)
p, _ := req.Conn.GetProperty("rid")
rid := p.(int)
if reqObj.Type == 0 {
//世界聊天
rsp.Body.Msg = this.worldGroup.PutMsg(reqObj.Msg, rid, 0)
}else if reqObj.Type == 1{
//联盟聊天
this.unionMutex.RLock()
id, ok := this.ridToUnionGroups[rid]
if ok {
g, ok := this.unionGroups[id]
if ok {
g.PutMsg(reqObj.Msg, rid, 1)
}else{
slog.Warn("chat not found rid in unionGroups", "rid", rid)
}
}else{
slog.Warn("chat not found rid in ridToUnionGroups", "rid", rid)
}
this.unionMutex.RUnlock()
}
}
//历史记录
func (this*Chat) history(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.HistoryReq{}
rspObj := &proto.HistoryRsp{}
rsp.Body.Code = constant.OK
mapstructure.Decode(req.Body.Msg, reqObj)
rspObj.Msgs = []proto.ChatMsg{}
p, _ := req.Conn.GetProperty("rid")
rid := p.(int)
if reqObj.Type == 0 {
r := this.worldGroup.History()
rspObj.Msgs = r
}else if reqObj.Type == 1 {
this.unionMutex.RLock()
id, ok := this.ridToUnionGroups[rid]
if ok {
g, ok := this.unionGroups[id]
if ok {
rspObj.Msgs = g.History()
}
}
this.unionMutex.RUnlock()
}
rspObj.Type = reqObj.Type
rsp.Body.Msg = rspObj
}
func (this*Chat) join(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.JoinReq{}
rspObj := &proto.JoinRsp{}
rsp.Body.Code = constant.OK
rsp.Body.Msg = rspObj
rspObj.Type = reqObj.Type
mapstructure.Decode(req.Body.Msg, reqObj)
p, _ := req.Conn.GetProperty("rid")
rid := p.(int)
if reqObj.Type == 1 {
u := this.worldGroup.GetUser(rid)
if u == nil {
rsp.Body.Code = constant.InvalidParam
return
}
this.unionMutex.Lock()
gId, ok := this.ridToUnionGroups[rid]
if ok {
if gId != reqObj.Id {
//联盟聊天只能有一个,顶掉旧的
if g,ok := this.unionGroups[gId]; ok {
g.Exit(rid)
}
_, ok = this.unionGroups[reqObj.Id]
if ok == false {
this.unionGroups[reqObj.Id] = logic.NewGroup()
}
this.ridToUnionGroups[rid] = reqObj.Id
this.unionGroups[reqObj.Id].Enter(u)
}
}else{
//新加入
_, ok = this.unionGroups[reqObj.Id]
if ok == false {
this.unionGroups[reqObj.Id] = logic.NewGroup()
}
this.ridToUnionGroups[rid] = reqObj.Id
this.unionGroups[reqObj.Id].Enter(u)
}
this.unionMutex.Unlock()
}
}
func (this*Chat) exit(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ExitReq{}
rspObj := &proto.ExitRsp{}
rsp.Body.Code = constant.OK
rsp.Body.Msg = rspObj
rspObj.Type = reqObj.Type
mapstructure.Decode(req.Body.Msg, reqObj)
p, _ := req.Conn.GetProperty("rid")
rid := p.(int)
if reqObj.Type == 1 {
this.unionMutex.Lock()
id, ok := this.ridToUnionGroups[rid]
if ok {
g, ok := this.unionGroups[id]
if ok {
g.Exit(rid)
}
}
delete(this.ridToUnionGroups, rid)
this.unionMutex.Unlock()
}
}

20
server/chatserver/init.go Normal file
View File

@@ -0,0 +1,20 @@
package chatserver
import (
"slgserver/db"
"slgserver/net"
"slgserver/server/chatserver/controller"
)
var MyRouter = &net.Router{}
func Init() {
db.TestDB()
//全部初始化完才注册路由,防止服务器还没启动就绪收到请求
initRouter()
}
func initRouter() {
controller.DefaultChat.InitRouter(MyRouter)
}

View File

@@ -0,0 +1,22 @@
package logic
import "time"
type User struct {
rid int
nickName string
}
func NewUser(rid int, nickName string) *User {
return &User{
rid: rid,
nickName: nickName,
}
}
type Msg struct {
RId int `json:"rid"`
NickName string `json:"nickName"`
Msg string `json:"msg"`
Time time.Time `json:"time"`
}

View File

@@ -0,0 +1,79 @@
package logic
import (
"slgserver/net"
"slgserver/server/chatserver/proto"
"sync"
"time"
)
type Group struct {
userMutex sync.RWMutex
msgMutex sync.RWMutex
users map[int]*User
msgs ItemQueue
}
func NewGroup() *Group {
return &Group{users: map[int]*User{}}
}
func (this*Group) Enter(u *User) {
this.userMutex.Lock()
defer this.userMutex.Unlock()
this.users[u.rid] = u
}
func (this*Group) Exit(rid int) {
this.userMutex.Lock()
defer this.userMutex.Unlock()
delete(this.users, rid)
}
func (this*Group) GetUser(rid int) *User {
this.userMutex.Lock()
defer this.userMutex.Unlock()
return this.users[rid]
}
func (this*Group) PutMsg(text string, rid int, t int8) *proto.ChatMsg {
this.userMutex.RLock()
u, ok := this.users[rid]
this.userMutex.RUnlock()
if ok == false{
return nil
}
msg := &Msg{Msg: text, RId: rid, Time: time.Now(), NickName: u.nickName}
this.msgMutex.Lock()
size := this.msgs.Size()
if size > 100 {
this.msgs.Dequeue()
}
this.msgs.Enqueue(msg)
this.msgMutex.Unlock()
//广播
this.userMutex.RLock()
c := &proto.ChatMsg{RId: msg.RId, NickName: msg.NickName, Time: msg.Time.Unix(), Msg: msg.Msg, Type: t}
for _, user := range this.users {
net.ConnMgr.PushByRoleId(user.rid, "chat.push", c)
}
this.userMutex.RUnlock()
return c
}
func (this*Group) History() []proto.ChatMsg {
r := make([]proto.ChatMsg, 0)
this.msgMutex.RLock()
items := this.msgs.items
for _, item := range items {
msg := item.(*Msg)
c := proto.ChatMsg{RId: msg.RId, NickName: msg.NickName, Time: msg.Time.Unix(), Msg: msg.Msg}
r = append(r, c)
}
this.msgMutex.RUnlock()
return r
}

View File

@@ -0,0 +1,45 @@
package logic
type Item interface {
}
// Item the type of the queue
type ItemQueue struct {
items []Item
}
type ItemQueuer interface {
New() ItemQueue
Enqueue(t Item)
Dequeue() *Item
IsEmpty() bool
Size() int
}
// New creates a new ItemQueue
func (s *ItemQueue) New() *ItemQueue {
s.items = []Item{}
return s
}
// Enqueue adds an Item to the end of the queue
func (s *ItemQueue) Enqueue(t Item) {
s.items = append(s.items, t)
}
// dequeue
func (s *ItemQueue) Dequeue() *Item {
item := s.items[0] // 先进先出
s.items = s.items[1:len(s.items)]
return &item
}
func (s *ItemQueue) IsEmpty() bool {
return len(s.items) == 0
}
// Size returns the number of Items in the queue
func (s *ItemQueue) Size() int {
return len(s.items)
}

View File

@@ -0,0 +1,64 @@
package proto
type LoginReq struct {
RId int `json:"rid"`
NickName string `json:"nickName"`
Token string `json:"token"`
}
type LoginRsp struct {
RId int `json:"rid"`
NickName string `json:"nickName"`
}
type LogoutReq struct {
RId int `json:"RId"`
}
type LogoutRsp struct {
RId int `json:"RId"`
}
type ChatReq struct {
Type int8 `json:"type"` //0世界聊天、1联盟聊天
Msg string `json:"msg"`
}
type ChatMsg struct {
RId int `json:"rid"`
NickName string `json:"nickName"`
Type int8 `json:"type"` //0世界聊天、1联盟聊天
Msg string `json:"msg"`
Time int64 `json:"time"`
}
type HistoryReq struct {
Type int8 `json:"type"` //0世界聊天、1联盟聊天
}
type HistoryRsp struct {
Type int8 `json:"type"` //0世界聊天、1联盟聊天
Msgs []ChatMsg `json:"msgs"`
}
type JoinReq struct {
Type int8 `json:"type"` //0世界聊天、1联盟聊天
Id int `json:"id"`
}
type JoinRsp struct {
Type int8 `json:"type"` //0世界聊天、1联盟聊天
Id int `json:"id"`
}
type ExitReq struct {
Type int8 `json:"type"` //0世界聊天、1联盟聊天
Id int `json:"id"`
}
type ExitRsp struct {
Type int8 `json:"type"` //0世界聊天、1联盟聊天
Id int `json:"id"`
}

View File

@@ -0,0 +1,205 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"log/slog"
"slgserver/config"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
chat_proto "slgserver/server/chatserver/proto"
"slgserver/server/slgserver/proto"
"strings"
"sync"
)
var GHandle = Handle{
proxys: make(map[string]map[int64]*net.ProxyClient),
}
type Handle struct {
proxyMutex sync.Mutex
proxys map[string]map[int64]*net.ProxyClient
slgProxy string
chatProxy string
loginProxy string
}
func isAccount(msgName string) bool {
sArr := strings.Split(msgName, ".")
prefix := ""
if len(sArr) == 2{
prefix = sArr[0]
}
if prefix == "account"{
return true
}else{
return false
}
}
func isChat(msgName string) bool {
sArr := strings.Split(msgName, ".")
prefix := ""
if len(sArr) == 2{
prefix = sArr[0]
}
if prefix == "chat"{
return true
}else{
return false
}
}
func (this*Handle) InitRouter(r *net.Router) {
this.init()
g := r.Group("*").Use(middleware.ElapsedTime(), middleware.Log())
g.AddRouter("*", this.all)
}
func (this*Handle) init() {
this.slgProxy = config.GetString("gateserver.slg_proxy", "ws://127.0.0.1:8001")
this.chatProxy = config.GetString("gateserver.chat_proxy", "ws://127.0.0.1:8002")
this.loginProxy = config.GetString("gateserver.login_proxy", "ws://127.0.0.1:8003")
}
func (this*Handle) onPush(conn *net.ClientConn, body *net.RspBody) {
gc, err := conn.GetProperty("gateConn")
if err != nil{
return
}
gateConn := gc.(net.WSConn)
gateConn.Push(body.Name, body.Msg)
}
func (this*Handle) onProxyClose(conn *net.ClientConn) {
p, err := conn.GetProperty("proxy")
if err == nil {
proxyStr := p.(string)
this.proxyMutex.Lock()
_, ok := this.proxys[proxyStr]
if ok {
c, err := conn.GetProperty("cid")
if err == nil{
cid := c.(int64)
delete(this.proxys[proxyStr], cid)
}
}
this.proxyMutex.Unlock()
}
}
func (this*Handle) OnServerConnClose (conn net.WSConn){
c, err := conn.GetProperty("cid")
arr := make([]*net.ProxyClient, 0)
if err == nil{
cid := c.(int64)
this.proxyMutex.Lock()
for _, m := range this.proxys {
proxy, ok := m[cid]
if ok {
arr = append(arr, proxy)
}
delete(m, cid)
}
this.proxyMutex.Unlock()
}
for _, client := range arr {
client.Close()
}
}
func (this*Handle) all(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
slog.Info("gateserver handle all begin",
"proxyStr", req.Body.Proxy,
"msgName", req.Body.Name)
this.deal(req, rsp)
if req.Body.Name == "role.enterServer" && rsp.Body.Code == constant.OK {
//登录聊天服
rspObj := &proto.EnterServerRsp{}
mapstructure.Decode(rsp.Body.Msg, rspObj)
r := &chat_proto.LoginReq{RId: rspObj.Role.RId, NickName: rspObj.Role.NickName, Token: rspObj.Token}
reqBody := &net.ReqBody{Seq: 0, Name: "chat.login", Msg: r, Proxy: ""}
rspBody := &net.RspBody{Seq: 0, Name: "chat.login", Msg: r, Code: 0}
this.deal(&net.WsMsgReq{Body: reqBody, Conn:req.Conn}, &net.WsMsgRsp{Body: rspBody})
}
slog.Info("gateserver handle all end",
"proxyStr", req.Body.Proxy,
"msgName", req.Body.Name)
}
func (this*Handle) deal(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
//协议转发
proxyStr := req.Body.Proxy
if isAccount(req.Body.Name){
proxyStr = this.loginProxy
}else if isChat(req.Body.Name){
proxyStr = this.chatProxy
} else{
proxyStr = this.slgProxy
}
if proxyStr == ""{
rsp.Body.Code = constant.ProxyNotInConnect
return
}
this.proxyMutex.Lock()
_, ok := this.proxys[proxyStr]
if ok == false {
this.proxys[proxyStr] = make(map[int64]*net.ProxyClient)
}
var err error
var proxy *net.ProxyClient
d, _ := req.Conn.GetProperty("cid")
cid := d.(int64)
proxy, ok = this.proxys[proxyStr][cid]
this.proxyMutex.Unlock()
if ok == false {
proxy = net.NewProxyClient(proxyStr)
this.proxyMutex.Lock()
this.proxys[proxyStr][cid] = proxy
this.proxyMutex.Unlock()
//发起链接,这里是阻塞的,所以不要上锁
err = proxy.Connect()
if err == nil{
proxy.SetProperty("cid", cid)
proxy.SetProperty("proxy", proxyStr)
proxy.SetProperty("gateConn", req.Conn)
proxy.SetOnPush(this.onPush)
proxy.SetOnClose(this.onProxyClose)
}
}
if err != nil {
this.proxyMutex.Lock()
delete(this.proxys[proxyStr], cid)
this.proxyMutex.Unlock()
rsp.Body.Code = constant.ProxyConnectError
return
}
rsp.Body.Seq = req.Body.Seq
rsp.Body.Name = req.Body.Name
r, err := proxy.Send(req.Body.Name, req.Body.Msg)
if err == nil{
rsp.Body.Code = r.Code
rsp.Body.Msg = r.Msg
}else{
rsp.Body.Code = constant.ProxyConnectError
rsp.Body.Msg = nil
}
}

19
server/gateserver/init.go Normal file
View File

@@ -0,0 +1,19 @@
package gateserver
import (
"slgserver/net"
"slgserver/server/gateserver/controller"
)
var MyRouter = &net.Router{}
func Init() {
//全部初始化完才注册路由,防止服务器还没启动就绪收到请求
initRouter()
}
func initRouter() {
controller.GHandle.InitRouter(MyRouter)
}

View File

@@ -0,0 +1,59 @@
package controller
import (
"github.com/gin-gonic/gin"
"net/http"
"log/slog"
"slgserver/constant"
myhttp "slgserver/server/httpserver"
"slgserver/server/httpserver/logic"
"slgserver/server/httpserver/middleware"
)
type AccountController struct{}
func (self AccountController) RegisterRoutes(g *gin.RouterGroup) {
g.Use(middleware.Cors())
g.Any("/account/register", self.register)
g.Any("/account/changepwd", self.changePwd)
g.Any("/account/forgetpwd", self.forgetPwd)
g.Any("/account/resetpwd", self.resetPwd)
}
func (self AccountController) register(ctx *gin.Context) {
slog.Info("register")
data := make(map[string]interface{})
if err := logic.DefaultUser.CreateUser(ctx); err != nil {
data["code"] = err.(*myhttp.MyError).Id()
data["errmsg"] = err.(*myhttp.MyError).Error()
}else{
data["code"] = constant.OK
}
ctx.JSON(http.StatusOK, data)
}
func (self AccountController) forgetPwd(ctx *gin.Context) {
slog.Info("forgetPwd")
ctx.String(http.StatusOK, "forgetPwd")
}
func (self AccountController) changePwd(ctx *gin.Context) {
slog.Info("changePwd")
data := make(map[string]interface{})
if err := logic.DefaultUser.ChangePassword(ctx); err != nil {
data["code"] = err.(*myhttp.MyError).Id()
data["errmsg"] = err.(*myhttp.MyError).Error()
}else{
data["code"] = constant.OK
}
ctx.JSON(http.StatusOK, data)
}
func (self AccountController) resetPwd(ctx *gin.Context) {
slog.Info("resetPwd")
ctx.String(http.StatusOK, "resetPwd")
}

View File

@@ -0,0 +1,88 @@
package logic
import (
"fmt"
"github.com/gin-gonic/gin"
"math/rand"
"slgserver/constant"
"slgserver/db"
myhttp "slgserver/server/httpserver"
"slgserver/server/loginserver/model"
"slgserver/util"
"time"
)
type UserLogic struct{}
var DefaultUser = UserLogic{}
func (self UserLogic) CreateUser(ctx *gin.Context) error {
account := ctx.Query("username")
pwd := ctx.Query("password")
hardware := ctx.Query("hardware")
if len(account) > 0 && len(pwd) > 0 {
if self.UserExists("username", account) {
return myhttp.New("账号已经存在", constant.UserExist)
}
passcode := fmt.Sprintf("%x", rand.Int31())
user := &model.User{
Username: account,
Passcode: passcode,
Passwd: util.Password(pwd, passcode),
Hardware: hardware,
Ctime: time.Now(),
Mtime: time.Now()}
if _, err := db.MasterDB.Insert(user); err != nil {
return myhttp.New("数据库出错", constant.DBError)
} else{
return nil
}
}else{
return myhttp.New("用户名或密码是空", constant.InvalidParam)
}
}
func (self UserLogic) ChangePassword(ctx *gin.Context) error {
account := ctx.Query("username")
pwd := ctx.Query("password")
newpwd := ctx.Query("newpassword")
user := &model.User{}
if len(account) > 0 && len(pwd) > 0 && len(newpwd) > 0{
if _, err := db.MasterDB.Where("username=?", account).Get(user); err != nil {
return myhttp.New("数据库出错", constant.DBError)
}else{
if util.Password(pwd, user.Passcode) == user.Passwd {
passcode := fmt.Sprintf("%x", rand.Int31())
changeData := map[string]interface{}{
"passwd": util.Password(newpwd, passcode),
"passcode": passcode,
"Mtime": time.Now(),
}
if _, err := db.MasterDB.Table(user).Where("username=?", account).Update(changeData); err !=nil {
return myhttp.New("数据库出错", constant.DBError)
}else{
return nil
}
}else{
return myhttp.New("原密码错误", constant.PwdIncorrect)
}
}
}else{
return myhttp.New("用户名或密码是空", constant.InvalidParam)
}
}
func (UserLogic) UserExists(field, val string) bool {
userLogin := &model.User{}
_, err := db.MasterDB.Where(field+"=?", val).Get(userLogin)
if err != nil || userLogin.UId == 0 {
return false
}
return true
}

View File

@@ -0,0 +1,22 @@
package middleware
import (
"github.com/gin-gonic/gin"
)
func Cors() gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
ctx.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
ctx.Writer.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS, POST, PUT, DELETE")
ctx.Writer.Header().Set("Content-Type", "application/json")
if ctx.Request.Method == "OPTIONS" {
ctx.AbortWithStatus(204)
return
}
ctx.Next()
}
}

View File

@@ -0,0 +1,21 @@
package httpserver
type MyError struct {
err string
id int
}
func New(err string, id int) error {
return &MyError{err: err, id: id}
}
func (self *MyError) Error() string {
return self.err
}
func (self *MyError) Id() int {
return self.id
}

View File

@@ -0,0 +1,189 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"slgserver/constant"
"slgserver/db"
"log/slog"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/loginserver/model"
"slgserver/server/loginserver/proto"
"slgserver/util"
"time"
)
var DefaultAccount = Account{}
type Account struct {
}
func (this*Account) InitRouter(r *net.Router) {
g := r.Group("account").Use(middleware.ElapsedTime(), middleware.Log())
g.AddRouter("login", this.login)
g.AddRouter("reLogin", this.reLogin)
g.AddRouter("logout", this.logout, middleware.CheckLogin())
g.AddRouter("serverList", this.serverList, middleware.CheckLogin())
}
func (this*Account) login(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.LoginReq{}
rspObj := &proto.LoginRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
user := &model.User{}
ok, err := db.MasterDB.Table(user).Where("username=?", reqObj.Username).Get(user)
if err!= nil {
slog.Error("login db error",
"username", reqObj.Username,
"error", err)
rsp.Body.Code = constant.DBError
}else{
if ok {
pwd := util.Password(reqObj.Password, user.Passcode)
if pwd != user.Passwd{
//密码不正确
slog.Warn("login password not right",
"username", user.Username)
rsp.Body.Code = constant.PwdIncorrect
}else{
tt := time.Now()
s := util.NewSession(user.UId, tt)
sessStr := s.String()
slog.Info("login",
"username", user.Username,
"session", sessStr)
//登录成功,写记录
lh := &model.LoginHistory{UId: user.UId, CTime: tt, Ip: reqObj.Ip,
Hardware: reqObj.Hardware, State: model.Login}
db.MasterDB.Insert(lh)
ll := &model.LoginLast{}
ok, _ := db.MasterDB.Table(ll).Where("uid=?", user.UId).Get(ll)
if ok {
ll.IsLogout = 0
ll.Ip = reqObj.Ip
ll.LoginTime = time.Now()
ll.Session = sessStr
ll.Hardware = reqObj.Hardware
_, err := db.MasterDB.ID(ll.Id).Cols(
"is_logout", "ip", "login_time", "session", "hardware").Update(ll)
if err != nil {
slog.Error("update login_last table fail", "error", err)
}
}else{
ll = &model.LoginLast{UId: user.UId, LoginTime: tt,
Ip: reqObj.Ip, Session: sessStr,
Hardware: reqObj.Hardware, IsLogout: 0}
db.MasterDB.Insert(ll)
}
rspObj.Session = sessStr
rspObj.Password = reqObj.Password
rspObj.Username = reqObj.Username
rspObj.UId = user.UId
rsp.Body.Code = constant.OK
net.ConnMgr.UserLogin(req.Conn, sessStr, ll.UId)
}
}else{
//数据库出错
slog.Warn("login username not found", "username", reqObj.Username)
rsp.Body.Code = constant.UserNotExist
}
}
}
func (this*Account) reLogin(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ReLoginReq{}
rspObj := &proto.ReLoginRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
if reqObj.Session == ""{
rsp.Body.Code = constant.SessionInvalid
return
}
rsp.Body.Msg = rspObj
rspObj.Session = reqObj.Session
sess, err := util.ParseSession(reqObj.Session)
if err != nil{
rsp.Body.Code = constant.SessionInvalid
}else{
if sess.IsValid() {
//数据库验证一下
ll := &model.LoginLast{}
db.MasterDB.Table(ll).Where("uid=?", sess.Id).Get(ll)
if ll.Session == reqObj.Session {
if ll.Hardware == reqObj.Hardware {
rsp.Body.Code = constant.OK
net.ConnMgr.UserLogin(req.Conn, reqObj.Session, ll.UId)
}else{
rsp.Body.Code = constant.HardwareIncorrect
}
}else{
rsp.Body.Code = constant.SessionInvalid
}
}else{
rsp.Body.Code = constant.SessionInvalid
}
}
}
func (this*Account) logout(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.LogoutReq{}
rspObj := &proto.LogoutRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.UId = reqObj.UId
rsp.Body.Code = constant.OK
slog.Info("logout", "uid", reqObj.UId)
tt := time.Now()
//登出,写记录
lh := &model.LoginHistory{UId: reqObj.UId, CTime: tt, State: model.Logout}
db.MasterDB.Insert(lh)
ll := &model.LoginLast{}
ok, _ := db.MasterDB.Table(ll).Where("uid=?", reqObj.UId).Get(ll)
if ok {
ll.IsLogout = 1
ll.LogoutTime = time.Now()
db.MasterDB.ID(ll.Id).Cols("is_logout", "logout_time").Update(ll)
}else{
ll = &model.LoginLast{UId: reqObj.UId, LogoutTime: tt, IsLogout: 0}
db.MasterDB.Insert(ll)
}
net.ConnMgr.UserLogout(req.Conn)
}
func (this*Account) serverList(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ServerListReq{}
rspObj := &proto.ServerListRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Code = constant.OK
s := proto.Server{Id: 1, Slg: "ws://127.0.0.1:8001", Chat: "ws://127.0.0.1:8002"}
r := make([]proto.Server, 0)
rspObj.Lists = append(r, s)
rsp.Body.Msg = rspObj
}

View File

@@ -0,0 +1,21 @@
package loginserver
import (
"slgserver/db"
"slgserver/net"
"slgserver/server/loginserver/controller"
)
var MyRouter = &net.Router{}
func Init() {
db.TestDB()
//全部初始化完才注册路由,防止服务器还没启动就绪收到请求
initRouter()
}
func initRouter() {
controller.DefaultAccount.InitRouter(MyRouter)
}

View File

@@ -0,0 +1,39 @@
package model
import (
"time"
)
const (
Login = iota
Logout
)
type LoginHistory struct {
Id int `xorm:"id pk autoincr"`
UId int `xorm:"uid"`
CTime time.Time `xorm:"ctime"`
Ip string `xorm:"ip"`
State int8 `xorm:"state"`
Hardware string `xorm:"hardware"`
}
func (this *LoginHistory) TableName() string {
return "tb_login_history"
}
type LoginLast struct {
Id int `xorm:"id pk autoincr"`
UId int `xorm:"uid"`
LoginTime time.Time `xorm:"login_time"`
LogoutTime time.Time `xorm:"logout_time"`
Ip string `xorm:"ip"`
Session string `xorm:"session"`
IsLogout int8 `xorm:"is_logout"`
Hardware string `xorm:"hardware"`
}
func (this *LoginLast) TableName() string {
return "tb_login_last"
}

View File

@@ -0,0 +1,19 @@
package model
import "time"
type User struct {
UId int `xorm:"uid pk autoincr"`
Username string `xorm:"username" validate:"min=4,max=20,regexp=^[a-zA-Z0-9_]*$"`
Passcode string `xorm:"passcode"`
Passwd string `xorm:"passwd"`
Hardware string `xorm:"hardware"`
Status int `xorm:"status"`
Ctime time.Time `xorm:"ctime"`
Mtime time.Time `xorm:"mtime"`
IsOnline bool `xorm:"-"`
}
func (this *User) TableName() string {
return "tb_user_info"
}

View File

@@ -0,0 +1,47 @@
package proto
type LoginReq struct {
Username string `json:"username"`
Password string `json:"password"`
Ip string `json:"ip"`
Hardware string `json:"hardware"`
}
type LoginRsp struct {
Username string `json:"username"`
Password string `json:"password"`
Session string `json:"session"`
UId int `json:"uid"`
}
type ReLoginReq struct {
Session string `json:"session"`
Ip string `json:"ip"`
Hardware string `json:"hardware"`
}
type ReLoginRsp struct {
Session string `json:"session"`
}
type LogoutReq struct {
UId int `json:"uid"`
}
type LogoutRsp struct {
UId int `json:"uid"`
}
type Server struct {
Id int `json:"id"`
Slg string `json:"slg"`
Chat string `json:"chat"`
}
type ServerListReq struct {
}
type ServerListRsp struct {
Lists []Server
}

View File

@@ -0,0 +1,12 @@
package conn
type PushSync interface {
IsCellView() bool //是否格子视野
IsCanView(rid, x, y int)bool //是否能出现在视野
BelongToRId() []int //属于的rid
PushMsgName() string //推送名字
Position() (int, int) //x, y
TPosition() (int, int) //目标x, y
ToProto() interface{} //转成proto
Push() //推送
}

View File

@@ -0,0 +1,569 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/global"
"slgserver/server/slgserver/logic"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/proto"
"slgserver/server/slgserver/static_conf"
"slgserver/server/slgserver/static_conf/facility"
"slgserver/server/slgserver/static_conf/general"
"time"
)
var DefaultArmy = Army{
}
type Army struct {
}
func (this*Army) InitRouter(r *net.Router) {
g := r.Group("army").Use(middleware.ElapsedTime(), middleware.Log(),
middleware.CheckLogin(), middleware.CheckRole())
g.AddRouter("myList", this.myList)
g.AddRouter("myOne", this.myOne)
g.AddRouter("dispose", this.dispose)
g.AddRouter("conscript", this.conscript)
g.AddRouter("assign", this.assign)
}
//我的军队列表
func (this*Army) myList(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ArmyListReq{}
rspObj := &proto.ArmyListRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
rspObj.CityId = reqObj.CityId
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
city,ok := mgr.RCMgr.Get(reqObj.CityId)
if ok == false{
rsp.Body.Code = constant.CityNotExist
return
}
if city.RId != role.RId {
rsp.Body.Code = constant.CityNotMe
return
}
as, _ := mgr.AMgr.GetByCity(reqObj.CityId)
rspObj.Armys = make([]proto.Army, len(as))
for i, v := range as {
rspObj.Armys[i] = v.ToProto().(proto.Army)
}
}
//我的某一个军队
func (this*Army) myOne(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ArmyOneReq{}
rspObj := &proto.ArmyOneRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
city, ok := mgr.RCMgr.Get(reqObj.CityId)
if ok == false{
rsp.Body.Code = constant.CityNotExist
return
}
if city.RId != role.RId {
rsp.Body.Code = constant.CityNotMe
return
}
a, ok := mgr.AMgr.GetByCityOrder(reqObj.CityId, reqObj.Order)
if ok {
rspObj.Army = a.ToProto().(proto.Army)
}else{
rsp.Body.Code = constant.ArmyNotFound
}
}
//配置武将
func (this*Army) dispose(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.DisposeReq{}
rspObj := &proto.DisposeRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if reqObj.Order <= 0 || reqObj.Order > 5 || reqObj.Position < -1 || reqObj.Position >2 {
rsp.Body.Code = constant.InvalidParam
return
}
city, ok := mgr.RCMgr.Get(reqObj.CityId)
if ok == false {
rsp.Body.Code = constant.CityNotExist
return
}
if city.RId != role.RId {
rsp.Body.Code = constant.CityNotMe
return
}
//校场每升一级一个队伍
jc, ok := mgr.RFMgr.GetFacility(city.CityId, facility.JiaoChang)
if ok == false || jc.GetLevel() < reqObj.Order {
rsp.Body.Code = constant.ArmyNotEnough
return
}
newG, ok := mgr.GMgr.GetByGId(reqObj.GeneralId)
if ok == false{
rsp.Body.Code = constant.GeneralNotFound
return
}
if newG.RId != role.RId{
rsp.Body.Code = constant.GeneralNotMe
return
}
army, err := mgr.AMgr.GetOrCreate(role.RId, reqObj.CityId, reqObj.Order)
if err != nil{
rsp.Body.Code = constant.DBError
return
}
if army.FromX != city.X || army.FromY != city.Y {
rsp.Body.Code = constant.ArmyIsOutside
return
}
//下阵
if reqObj.Position == -1{
for pos, g := range army.Gens {
if g != nil && g.Id == newG.Id{
//征兵中不能下阵
if army.PositionCanModify(pos) == false{
if army.Cmd == model.ArmyCmdConscript{
rsp.Body.Code = constant.GeneralBusy
}else{
rsp.Body.Code = constant.ArmyBusy
}
return
}
army.GeneralArray[pos] = 0
army.SoldierArray[pos] = 0
army.Gens[pos] = nil
army.SyncExecute()
break
}
}
newG.Order = 0
newG.CityId = 0
newG.SyncExecute()
}else{
//征兵中不能下阵
if army.PositionCanModify(reqObj.Position) == false{
if army.Cmd == model.ArmyCmdConscript{
rsp.Body.Code = constant.GeneralBusy
}else{
rsp.Body.Code = constant.ArmyBusy
}
return
}
if newG.CityId != 0{
rsp.Body.Code = constant.GeneralBusy
return
}
if mgr.AMgr.IsRepeat(role.RId, newG.CfgId) == false{
rsp.Body.Code = constant.GeneralRepeat
return
}
//判断是否能配前锋
tst, ok := mgr.RFMgr.GetFacility(city.CityId, facility.TongShuaiTing)
if reqObj.Position == 2 && ( ok == false || tst.GetLevel() < reqObj.Order) {
rsp.Body.Code = constant.TongShuaiNotEnough
return
}
//判断cost
cost := general.General.Cost(newG.CfgId)
for i, g := range army.Gens {
if g == nil || i == reqObj.Position {
continue
}
cost += general.General.Cost(g.CfgId)
}
if mgr.GetCityCost(city.CityId) < cost{
rsp.Body.Code = constant.CostNotEnough
return
}
oldG := army.Gens[reqObj.Position]
if oldG != nil {
//旧的下阵
oldG.CityId = 0
oldG.Order = 0
oldG.SyncExecute()
}
//新的上阵
army.GeneralArray[reqObj.Position] = reqObj.GeneralId
army.Gens[reqObj.Position] = newG
army.SoldierArray[reqObj.Position] = 0
newG.Order = reqObj.Order
newG.CityId = reqObj.CityId
newG.SyncExecute()
}
army.FromX = city.X
army.FromY = city.Y
army.SyncExecute()
//队伍
rspObj.Army = army.ToProto().(proto.Army)
}
//征兵
func (this*Army) conscript(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ConscriptReq{}
rspObj := &proto.ConscriptRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
if reqObj.ArmyId <= 0 || len(reqObj.Cnts) != 3 ||
reqObj.Cnts[0] < 0 || reqObj.Cnts[1] < 0 || reqObj.Cnts[2] < 0{
rsp.Body.Code = constant.InvalidParam
return
}
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
army,ok := mgr.AMgr.Get(reqObj.ArmyId)
if ok == false{
rsp.Body.Code = constant.ArmyNotFound
return
}
if role.RId != army.RId{
rsp.Body.Code = constant.ArmyNotMe
return
}
//判断该位置是否能征兵
for pos, cnt := range reqObj.Cnts {
if cnt > 0{
if army.Gens[pos] == nil{
rsp.Body.Code = constant.InvalidParam
return
}
if army.PositionCanModify(pos) == false{
rsp.Body.Code = constant.GeneralBusy
return
}
}
}
lv := mgr.RFMgr.GetFacilityLv(army.CityId, facility.MBS)
if lv <= 0{
rsp.Body.Code = constant.BuildMBSNotFound
return
}
//判断是否超过上限
for i, g := range army.Gens {
if g == nil {
continue
}
l, e := general.GenBasic.GetLevel(g.Level)
add := mgr.RFMgr.GetAdditions(army.CityId, facility.TypeSoldierLimit)
if e == nil{
if l.Soldiers + add[0] < reqObj.Cnts[i]+army.SoldierArray[i]{
rsp.Body.Code = constant.OutArmyLimit
return
}
}else{
rsp.Body.Code = constant.InvalidParam
return
}
}
//开始征兵
total := 0
for _, n := range reqObj.Cnts {
total += n
}
conscript := static_conf.Basic.ConScript
needWood := total*conscript.CostWood
needGrain := total*conscript.CostGrain
needIron := total*conscript.CostIron
needStone := total*conscript.CostStone
needGold := total*conscript.CostGold
nr := facility.NeedRes{Grain: needGrain, Wood: needWood,
Gold: needGold, Iron: needIron, Decree: 0,
Stone: needStone}
if code := mgr.RResMgr.TryUseNeed(army.RId, nr); code == constant.OK {
curTime := time.Now().Unix()
for i, _ := range army.SoldierArray {
if reqObj.Cnts[i] > 0{
army.ConscriptCntArray[i] = reqObj.Cnts[i]
army.ConscriptTimeArray[i] = int64(reqObj.Cnts[i]*conscript.CostTime) + curTime - 2
}
}
army.Cmd = model.ArmyCmdConscript
army.SyncExecute()
//队伍
rspObj.Army = army.ToProto().(proto.Army)
//资源
if rRes, ok := mgr.RResMgr.Get(role.RId); ok {
rspObj.RoleRes = rRes.ToProto().(proto.RoleRes)
}
rsp.Body.Code = constant.OK
}else{
rsp.Body.Code = constant.ResNotEnough
}
}
//派遣队伍
func (this*Army) assign(req *net.WsMsgReq, rsp *net.WsMsgRsp){
reqObj := &proto.AssignArmyReq{}
rspObj := &proto.AssignArmyRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
army,ok := mgr.AMgr.Get(reqObj.ArmyId)
if ok == false{
rsp.Body.Code = constant.ArmyNotFound
return
}
if role.RId != army.RId{
rsp.Body.Code = constant.ArmyNotMe
return
}
if reqObj.Cmd == model.ArmyCmdBack{
rsp.Body.Code = this.__back__(army)
}else if reqObj.Cmd == model.ArmyCmdAttack {
rsp.Body.Code = this.__attack__(reqObj, army, role)
}else if reqObj.Cmd == model.ArmyCmdDefend {
rsp.Body.Code = this.__defend__(reqObj, army, role)
}else if reqObj.Cmd == model.ArmyCmdReclamation {
rsp.Body.Code = this.__reclamation__(reqObj, army, role)
}else if reqObj.Cmd == model.ArmyCmdTransfer {
rsp.Body.Code = this.__transfer__(reqObj, army, role)
}
rspObj.Army = army.ToProto().(proto.Army)
}
func (this*Army) __pre__(reqObj *proto.AssignArmyReq, army* model.Army, role *model.Role) int{
if reqObj.X < 0 || reqObj.X >= global.MapWith ||
reqObj.Y < 0 || reqObj.Y >= global.MapHeight {
return constant.InvalidParam
}
if army.IsCanOutWar() == false{
if army.Cmd != model.ArmyCmdIdle{
return constant.ArmyBusy
}else{
return constant.ArmyNotMain
}
}
if army.IsIdle() == false {
return constant.ArmyBusy
}
//判断该地是否是能攻击类型
cfg, ok := mgr.NMMgr.PositionBuild(reqObj.X, reqObj.Y)
if ok == false || cfg.Type == 0 {
return constant.InvalidParam
}
if logic.IsCanArrive(reqObj.X, reqObj.Y, role.RId) == false{
return constant.UnReachable
}
return constant.OK
}
func (this*Army) __after__(reqObj *proto.AssignArmyReq, army* model.Army) int{
//最后才消耗体力
cost := static_conf.Basic.General.CostPhysicalPower
ok := mgr.GMgr.PhysicalPowerIsEnough(army, cost)
if ok == false{
return constant.PhysicalPowerNotEnough
}
if reqObj.Cmd == model.ArmyCmdReclamation || reqObj.Cmd == model.ArmyCmdTransfer {
cost := static_conf.Basic.General.ReclamationCost
if mgr.RResMgr.DecreeIsEnough(army.RId, cost) == false{
return constant.DecreeNotEnough
}else{
mgr.RResMgr.TryUseDecree(army.RId, cost)
}
}
mgr.GMgr.TryUsePhysicalPower(army, cost)
army.ToX = reqObj.X
army.ToY = reqObj.Y
army.Cmd = reqObj.Cmd
army.State = model.ArmyRunning
//speed := mgr.AMgr.GetSpeed(army)
//t := mgr.TravelTime(speed, army.FromX, army.FromY, army.ToX, army.ToY)
army.Start = time.Now()
//army.End = time.Now().Add(time.Duration(t) * time.Millisecond)
army.End = time.Now().Add(40*time.Second)
logic.ArmyLogic.PushAction(army)
return constant.OK
}
//返回
func (this*Army) __back__(army* model.Army) int{
if army.Cmd == model.ArmyCmdAttack ||
army.Cmd == model.ArmyCmdDefend ||
army.Cmd == model.ArmyCmdReclamation {
logic.ArmyLogic.ArmyBack(army)
}else if army.IsIdle(){
city, ok := mgr.RCMgr.Get(army.CityId)
if ok {
if city.X != army.FromX || city.Y != army.FromY{
logic.ArmyLogic.ArmyBack(army)
}
}
}
return constant.OK
}
//攻击
func (this*Army) __attack__(reqObj *proto.AssignArmyReq, army* model.Army, role *model.Role) int{
code := this.__pre__(reqObj, army, role)
if code != constant.OK {
return code
}
if logic.IsCanArrive(reqObj.X, reqObj.Y, role.RId) == false{
return constant.UnReachable
}
//免战
if logic.IsWarFree(reqObj.X, reqObj.Y){
return constant.BuildWarFree
}
if logic.IsCanDefend(reqObj.X, reqObj.Y, role.RId) == true {
return constant.BuildCanNotAttack
}
return this.__after__(reqObj, army)
}
//驻守
func (this*Army) __defend__(reqObj *proto.AssignArmyReq, army* model.Army, role *model.Role) int{
code := this.__pre__(reqObj, army, role)
if code != constant.OK {
return code
}
if logic.IsCanDefend(reqObj.X, reqObj.Y, role.RId) == false {
return constant.BuildCanNotDefend
}
return this.__after__(reqObj, army)
}
func (this*Army) __reclamation__(reqObj *proto.AssignArmyReq, army* model.Army, role *model.Role) int{
code := this.__pre__(reqObj, army, role)
if code != constant.OK {
return code
}
if mgr.RBMgr.BuildIsRId(reqObj.X, reqObj.Y, role.RId) == false {
return constant.BuildNotMe
}
return this.__after__(reqObj, army)
}
func (this*Army) __transfer__(reqObj *proto.AssignArmyReq, army* model.Army, role *model.Role) int{
code := this.__pre__(reqObj, army, role)
if code != constant.OK {
return code
}
if army.FromX == reqObj.X && army.FromY == reqObj.Y{
return constant.CanNotTransfer
}
if mgr.RBMgr.BuildIsRId(reqObj.X, reqObj.Y, role.RId) == false {
return constant.BuildNotMe
}
b, ok := mgr.RBMgr.PositionBuild(reqObj.X, reqObj.Y)
if ok == false {
return constant.BuildNotMe
}
if b.Level <= 0 || b.IsHasTransferAuth() == false {
return constant.CanNotTransfer
}
cnt := 0
if b.IsRoleFortress() {
cnt = static_conf.MapBCConf.GetHoldArmyCnt(b.Type, b.Level)
}else{
cnt = 5
}
if cnt > mgr.AMgr.BelongPosArmyCnt(b.RId, b.X, b.Y) {
return this.__after__(reqObj, army)
}else{
return constant.HoldIsFull
}
}

View File

@@ -0,0 +1,109 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/proto"
)
var DefaultCity = City{
}
type City struct {
}
func (this*City) InitRouter(r *net.Router) {
g := r.Group("city").Use(middleware.ElapsedTime(), middleware.Log(),
middleware.CheckLogin(), middleware.CheckRole())
g.AddRouter("facilities", this.facilities)
g.AddRouter("upFacility", this.upFacility)
}
func (this*City) facilities(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.FacilitiesReq{}
rspObj := &proto.FacilitiesRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.CityId = reqObj.CityId
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
city, ok := mgr.RCMgr.Get(reqObj.CityId)
if ok == false {
rsp.Body.Code = constant.CityNotExist
return
}
role := r.(*model.Role)
if city.RId != role.RId {
rsp.Body.Code = constant.CityNotMe
return
}
f, ok := mgr.RFMgr.Get(reqObj.CityId)
if ok == false {
rsp.Body.Code = constant.CityNotExist
return
}
t := f.Facility()
rspObj.Facilities = make([]proto.Facility, len(t))
for i, v := range t {
rspObj.Facilities[i].Name = v.Name
rspObj.Facilities[i].Level = v.GetLevel()
rspObj.Facilities[i].Type = v.Type
rspObj.Facilities[i].UpTime = v.UpTime
}
}
//升级需要耗费时间,为了减少定时任务,升级这里做成被动触发产生,不做定时任务
func (this*City) upFacility(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.UpFacilityReq{}
rspObj := &proto.UpFacilityRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.CityId = reqObj.CityId
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
city, ok := mgr.RCMgr.Get(reqObj.CityId)
if ok == false {
rsp.Body.Code = constant.CityNotExist
return
}
role := r.(*model.Role)
if city.RId != role.RId {
rsp.Body.Code = constant.CityNotMe
return
}
_, ok = mgr.RFMgr.Get(reqObj.CityId)
if ok == false {
rsp.Body.Code = constant.CityNotExist
return
}
out, errCode := mgr.RFMgr.UpFacility(role.RId ,reqObj.CityId, reqObj.FType)
rsp.Body.Code = errCode
if errCode == constant.OK{
rspObj.Facility.Level = out.GetLevel()
rspObj.Facility.Type = out.Type
rspObj.Facility.Name = out.Name
rspObj.Facility.UpTime = out.UpTime
if roleRes, ok:= mgr.RResMgr.Get(role.RId); ok {
rspObj.RoleRes = roleRes.ToProto().(proto.RoleRes)
}
}
}

View File

@@ -0,0 +1,688 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"log/slog"
"slgserver/constant"
"slgserver/db"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/logic"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/proto"
"slgserver/server/slgserver/static_conf"
"time"
)
var DefaultCoalition= coalition{
}
type coalition struct {
}
func (this *coalition) InitRouter(r *net.Router) {
g := r.Group("union").Use(middleware.ElapsedTime(), middleware.Log(),
middleware.CheckLogin(), middleware.CheckRole())
g.AddRouter("create", this.create)
g.AddRouter("list", this.list)
g.AddRouter("join", this.join)
g.AddRouter("verify", this.verify)
g.AddRouter("member", this.member)
g.AddRouter("applyList", this.applyList)
g.AddRouter("exit", this.exit)
g.AddRouter("dismiss", this.dismiss)
g.AddRouter("notice", this.notice)
g.AddRouter("modNotice", this.modNotice)
g.AddRouter("kick", this.kick)
g.AddRouter("appoint", this.appoint)
g.AddRouter("abdicate", this.abdicate)
g.AddRouter("info", this.info)
g.AddRouter("log", this.log)
}
//创建联盟
func (this *coalition) create(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.CreateReq{}
rspObj := &proto.CreateRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
rspObj.Name = reqObj.Name
has := mgr.RAttrMgr.IsHasUnion(role.RId)
if has {
rsp.Body.Code = constant.UnionAlreadyHas
return
}
c, ok := mgr.UnionMgr.Create(reqObj.Name, role.RId)
if ok {
rspObj.Id = c.Id
logic.Union.MemberEnter(role.RId, c.Id)
model.NewCreate(role.NickName, c.Id, role.RId)
}else{
rsp.Body.Code = constant.UnionCreateError
}
}
//联盟列表
func (this *coalition) list(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ListReq{}
rspObj := &proto.ListRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
l := mgr.UnionMgr.List()
rspObj.List = make([]proto.Union, len(l))
for i, u := range l {
rspObj.List[i] = u.ToProto().(proto.Union)
main := make([]proto.Major, 0)
if r, ok := mgr.RMgr.Get(u.Chairman); ok {
m := proto.Major{Name: r.NickName, RId: r.RId, Title: proto.UnionChairman}
main = append(main, m)
}
if r, ok := mgr.RMgr.Get(u.ViceChairman); ok {
m := proto.Major{Name: r.NickName, RId: r.RId, Title: proto.UnionViceChairman}
main = append(main, m)
}
rspObj.List[i].Major = main
}
}
//加入
func (this *coalition) join(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.JoinReq{}
rspObj := &proto.JoinRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
has := mgr.RAttrMgr.IsHasUnion(role.RId)
if has {
rsp.Body.Code = constant.UnionAlreadyHas
return
}
u, ok := mgr.UnionMgr.Get(reqObj.Id)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
if len(u.MemberArray) >= static_conf.Basic.Union.MemberLimit{
rsp.Body.Code = constant.PeopleIsFull
return
}
//判断当前是否已经有申请
has, _ = db.MasterDB.Table(model.CoalitionApply{}).Where(
"union_id=? and state=? and rid=?",
reqObj.Id, proto.UnionUntreated, role.RId).Get(&model.CoalitionApply{})
if has {
rsp.Body.Code = constant.HasApply
return
}
//写入申请列表
apply := &model.CoalitionApply{
RId: role.RId,
UnionId: reqObj.Id,
Ctime: time.Now(),
State: proto.UnionUntreated}
_, err := db.MasterDB.InsertOne(apply)
if err != nil{
rsp.Body.Code = constant.DBError
slog.Warn("db error", "error", err)
return
}
//推送主、副盟主
apply.SyncExecute()
}
//审核
func (this *coalition) verify(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.VerifyReq{}
rspObj := &proto.VerifyRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.Id = reqObj.Id
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
apply := &model.CoalitionApply{}
ok, err := db.MasterDB.Table(model.CoalitionApply{}).Where(
"id=? and state=?", reqObj.Id, proto.UnionUntreated).Get(apply)
if ok && err == nil{
targetRole, ok := mgr.RMgr.Get(apply.RId)
if ok == false{
rsp.Body.Code = constant.RoleNotExist
return
}
if u, ok := mgr.UnionMgr.Get(apply.UnionId); ok {
if u.Chairman != role.RId && u.ViceChairman != role.RId {
rsp.Body.Code = constant.PermissionDenied
return
}
if len(u.MemberArray) >= static_conf.Basic.Union.MemberLimit{
rsp.Body.Code = constant.PeopleIsFull
return
}
if ok := mgr.RAttrMgr.IsHasUnion(apply.RId); ok {
rsp.Body.Code = constant.UnionAlreadyHas
}else{
if reqObj.Decide == proto.UnionAdopt {
//同意
c, ok := mgr.UnionMgr.Get(apply.UnionId)
if ok {
c.MemberArray = append(c.MemberArray, apply.RId)
logic.Union.MemberEnter(apply.RId, apply.UnionId)
c.SyncExecute()
model.NewJoin(targetRole.NickName, apply.UnionId, role.RId, apply.RId)
}
}
}
apply.State = reqObj.Decide
db.MasterDB.Table(apply).ID(apply.Id).Cols("state").Update(apply)
}else{
rsp.Body.Code = constant.UnionNotFound
return
}
}else{
rsp.Body.Code = constant.InvalidParam
}
}
//成员列表
func (this *coalition) member(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.MemberReq{}
rspObj := &proto.MemberRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.Id = reqObj.Id
rsp.Body.Code = constant.OK
union, ok := mgr.UnionMgr.Get(reqObj.Id)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
rspObj.Members = make([]proto.Member, 0)
for _, rid := range union.MemberArray {
if role, ok := mgr.RMgr.Get(rid); ok {
m := proto.Member{RId: role.RId, Name: role.NickName }
if main, ok := mgr.RCMgr.GetMainCity(role.RId); ok {
m.X = main.X
m.Y = main.Y
}
if rid == union.Chairman {
m.Title = proto.UnionChairman
}else if rid == union.ViceChairman {
m.Title = proto.UnionViceChairman
}else {
m.Title = proto.UnionCommon
}
rspObj.Members = append(rspObj.Members, m)
}
}
}
//申请列表
func (this *coalition) applyList(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ApplyReq{}
rspObj := &proto.ApplyRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
u, ok := mgr.UnionMgr.Get(reqObj.Id)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
if u.Chairman != role.RId && u.ViceChairman != role.RId {
rspObj.Applys = make([]proto.ApplyItem, 0)
return
}
applys := make([]*model.CoalitionApply, 0)
err := db.MasterDB.Table(model.CoalitionApply{}).Where(
"union_id=? and state=?", reqObj.Id, 0).Find(&applys)
if err != nil{
rsp.Body.Code = constant.DBError
return
}
rspObj.Id = reqObj.Id
rspObj.Applys = make([]proto.ApplyItem, 0)
for _, apply := range applys {
if r, ok := mgr.RMgr.Get(apply.RId);ok{
a := proto.ApplyItem{Id: apply.Id, RId: apply.RId, NickName: r.NickName}
rspObj.Applys = append(rspObj.Applys, a)
}
}
}
//退出
func (this *coalition) exit(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ExitReq{}
rspObj := &proto.ExitRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if ok := mgr.RAttrMgr.IsHasUnion(role.RId); ok == false {
rsp.Body.Code = constant.UnionNotFound
return
}
attribute, _ := mgr.RAttrMgr.Get(role.RId)
u, ok := mgr.UnionMgr.Get(attribute.UnionId)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
//盟主不能退出
if u.Chairman == role.RId {
rsp.Body.Code = constant.UnionNotAllowExit
return
}
for i, rid := range u.MemberArray {
if rid == role.RId{
u.MemberArray = append(u.MemberArray[:i], u.MemberArray[i+1:]...)
}
}
if u.ViceChairman == role.RId{
u.ViceChairman = 0
}
logic.Union.MemberExit(role.RId)
u.SyncExecute()
model.NewExit(role.NickName, u.Id, role.RId)
}
//解散
func (this *coalition) dismiss(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.DismissReq{}
rspObj := &proto.DismissRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if ok := mgr.RAttrMgr.IsHasUnion(role.RId); ok == false {
rsp.Body.Code = constant.UnionNotFound
return
}
attribute, _ := mgr.RAttrMgr.Get(role.RId)
u, ok := mgr.UnionMgr.Get(attribute.UnionId)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
//盟主才能解散
if u.Chairman != role.RId {
rsp.Body.Code = constant.PermissionDenied
return
}
unionId := attribute.UnionId
logic.Union.Dismiss(unionId)
model.NewDismiss(role.NickName, unionId, role.RId)
}
//公告
func (this *coalition) notice(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.NoticeReq{}
rspObj := &proto.NoticeRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
u, ok := mgr.UnionMgr.Get(reqObj.Id)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
rspObj.Text = u.Notice
}
//修改公告
func (this *coalition) modNotice(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ModNoticeReq{}
rspObj := &proto.ModNoticeRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if len(reqObj.Text) > 200 {
rsp.Body.Code = constant.ContentTooLong
return
}
if ok := mgr.RAttrMgr.IsHasUnion(role.RId); ok == false {
rsp.Body.Code = constant.UnionNotFound
return
}
attribute, _ := mgr.RAttrMgr.Get(role.RId)
u, ok := mgr.UnionMgr.Get(attribute.UnionId)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
if u.Chairman != role.RId && u.ViceChairman != role.RId {
rsp.Body.Code = constant.PermissionDenied
return
}
rspObj.Text = reqObj.Text
rspObj.Id = u.Id
u.Notice = reqObj.Text
u.SyncExecute()
model.NewModNotice(role.NickName, u.Id, role.RId)
}
//踢人
func (this *coalition) kick(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.KickReq{}
rspObj := &proto.KickRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.RId = reqObj.RId
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if ok := mgr.RAttrMgr.IsHasUnion(role.RId); ok == false {
rsp.Body.Code = constant.UnionNotFound
return
}
opAr, _ := mgr.RAttrMgr.Get(role.RId)
u, ok := mgr.UnionMgr.Get(opAr.UnionId)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
if u.Chairman != role.RId && u.ViceChairman != role.RId {
rsp.Body.Code = constant.PermissionDenied
return
}
if role.RId == reqObj.RId {
rsp.Body.Code = constant.PermissionDenied
return
}
targetRole, ok := mgr.RMgr.Get(reqObj.RId)
if ok == false {
rsp.Body.Code = constant.RoleNotExist
return
}
target, ok := mgr.RAttrMgr.Get(reqObj.RId)
if ok {
if target.UnionId == u.Id{
for i, rid := range u.MemberArray {
if rid == reqObj.RId{
u.MemberArray = append(u.MemberArray[:i], u.MemberArray[i+1:]...)
}
}
if u.ViceChairman == reqObj.RId{
u.ViceChairman = 0
}
logic.Union.MemberExit(reqObj.RId)
target.UnionId = 0
u.SyncExecute()
model.NewKick(role.NickName, targetRole.NickName, u.Id, role.RId, target.RId)
}else{
rsp.Body.Code = constant.NotBelongUnion
}
}else{
rsp.Body.Code = constant.NotBelongUnion
}
}
//任命
func (this *coalition) appoint(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.AppointReq{}
rspObj := &proto.AppointRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.RId = reqObj.RId
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if ok := mgr.RAttrMgr.IsHasUnion(role.RId); ok == false {
rsp.Body.Code = constant.UnionNotFound
return
}
opAr, _ := mgr.RAttrMgr.Get(role.RId)
u, ok := mgr.UnionMgr.Get(opAr.UnionId)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
if u.Chairman != role.RId {
rsp.Body.Code = constant.PermissionDenied
return
}
targetRole, ok := mgr.RMgr.Get(reqObj.RId)
if ok == false {
rsp.Body.Code = constant.RoleNotExist
return
}
target, ok := mgr.RAttrMgr.Get(reqObj.RId)
if ok {
if target.UnionId == u.Id{
if reqObj.Title == proto.UnionViceChairman {
u.ViceChairman = reqObj.RId
rspObj.Title = reqObj.Title
u.SyncExecute()
model.NewAppoint(role.NickName, targetRole.NickName, u.Id, role.RId, targetRole.RId, reqObj.Title)
}else if reqObj.Title == proto.UnionCommon {
if u.ViceChairman == reqObj.RId{
u.ViceChairman = 0
}
rspObj.Title = reqObj.Title
model.NewAppoint(role.NickName, targetRole.NickName, u.Id, role.RId, targetRole.RId, reqObj.Title)
}else{
rsp.Body.Code = constant.InvalidParam
}
}else{
rsp.Body.Code = constant.NotBelongUnion
}
}else{
rsp.Body.Code = constant.NotBelongUnion
}
}
//禅让
func (this *coalition) abdicate(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.AbdicateReq{}
rspObj := &proto.AbdicateRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if ok := mgr.RAttrMgr.IsHasUnion(role.RId); ok == false {
rsp.Body.Code = constant.UnionNotFound
return
}
opAr, _ := mgr.RAttrMgr.Get(role.RId)
u, ok := mgr.UnionMgr.Get(opAr.UnionId)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
targetRole, ok := mgr.RMgr.Get(reqObj.RId)
if ok == false {
rsp.Body.Code = constant.RoleNotExist
return
}
if u.Chairman != role.RId && u.ViceChairman != role.RId {
rsp.Body.Code = constant.PermissionDenied
return
}
target, ok := mgr.RAttrMgr.Get(reqObj.RId)
if ok {
if target.UnionId == u.Id{
if role.RId == u.Chairman{
u.Chairman = reqObj.RId
if u.ViceChairman == reqObj.RId{
u.ViceChairman = 0
}
u.SyncExecute()
model.NewAbdicate(role.NickName, targetRole.NickName, u.Id,
role.RId, targetRole.RId, proto.UnionChairman)
}else if role.RId == u.ViceChairman {
u.ViceChairman = reqObj.RId
u.SyncExecute()
model.NewAbdicate(role.NickName, targetRole.NickName, u.Id,
role.RId, targetRole.RId, proto.UnionViceChairman)
}
}else{
rsp.Body.Code = constant.NotBelongUnion
}
}else{
rsp.Body.Code = constant.NotBelongUnion
}
}
//联盟信息
func (this *coalition) info(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.InfoReq{}
rspObj := &proto.InfoRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.Id = reqObj.Id
rsp.Body.Code = constant.OK
u, ok := mgr.UnionMgr.Get(reqObj.Id)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
}else{
rspObj.Info = u.ToProto().(proto.Union)
main := make([]proto.Major, 0)
if r, ok := mgr.RMgr.Get(u.Chairman); ok {
m := proto.Major{Name: r.NickName, RId: r.RId, Title: proto.UnionChairman}
main = append(main, m)
}
if r, ok := mgr.RMgr.Get(u.ViceChairman); ok {
m := proto.Major{Name: r.NickName, RId: r.RId, Title: proto.UnionViceChairman}
main = append(main, m)
}
rspObj.Info.Major = main
}
}
//联盟日志
func (this *coalition) log(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.LogReq{}
rspObj := &proto.LogRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rspObj.Logs = make([]proto.UnionLog, 0)
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
opAr, _ := mgr.RAttrMgr.Get(role.RId)
u, ok := mgr.UnionMgr.Get(opAr.UnionId)
if ok == false{
rsp.Body.Code = constant.UnionNotFound
return
}
//开始查询日志
logs := make([]*model.CoalitionLog, 0)
err := db.MasterDB.Table(model.CoalitionLog{}).Where(
"union_id=?", u.Id).Desc("ctime").Find(&logs)
if err != nil{
slog.Warn("db error", "error", err)
}
for _, cLog := range logs {
rspObj.Logs = append(rspObj.Logs, cLog.ToProto().(proto.UnionLog))
}
}

View File

@@ -0,0 +1,385 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/proto"
"slgserver/server/slgserver/static_conf"
"slgserver/server/slgserver/static_conf/skill"
)
var DefaultGeneral = General{
}
type General struct {
}
func (this*General) InitRouter(r *net.Router) {
g := r.Group("general").Use(middleware.ElapsedTime(), middleware.Log(),
middleware.CheckLogin(), middleware.CheckRole())
g.AddRouter("myGenerals", this.myGenerals)
g.AddRouter("drawGeneral", this.drawGenerals)
g.AddRouter("composeGeneral", this.composeGeneral)
g.AddRouter("addPrGeneral", this.addPrGeneral)
g.AddRouter("convert", this.convert)
g.AddRouter("upSkill", this.upSkill)
g.AddRouter("downSkill", this.downSkill)
g.AddRouter("lvSkill", this.lvSkill)
}
func (this*General) myGenerals(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.MyGeneralReq{}
rspObj := &proto.MyGeneralRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
gs, ok := mgr.GMgr.GetOrCreateByRId(role.RId)
if ok {
rsp.Body.Code = constant.OK
rspObj.Generals = make([]proto.General, 0)
for _, v := range gs {
rspObj.Generals = append(rspObj.Generals, v.ToProto().(proto.General))
}
}else{
rsp.Body.Code = constant.DBError
}
}
func (this*General) drawGenerals(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.DrawGeneralReq{}
rspObj := &proto.DrawGeneralRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
cost := static_conf.Basic.General.DrawGeneralCost * reqObj.DrawTimes
ok := mgr.RResMgr.GoldIsEnough(role.RId,cost)
if ok == false{
rsp.Body.Code = constant.GoldNotEnough
return
}
limit := static_conf.Basic.General.Limit
cnt := mgr.GMgr.Count(role.RId)
if cnt + reqObj.DrawTimes > limit{
rsp.Body.Code = constant.OutGeneralLimit
return
}
gs, ok := mgr.GMgr.RandCreateGeneral(role.RId,reqObj.DrawTimes)
if ok {
mgr.RResMgr.TryUseGold(role.RId, cost)
rsp.Body.Code = constant.OK
rspObj.Generals = make([]proto.General, len(gs))
for i, v := range gs {
rspObj.Generals[i] = v.ToProto().(proto.General)
}
}else{
rsp.Body.Code = constant.DBError
}
}
func (this*General) composeGeneral(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ComposeGeneralReq{}
rspObj := &proto.ComposeGeneralRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
gs, ok := mgr.GMgr.HasGeneral(role.RId,reqObj.CompId)
//是否有这个武将
if ok == false{
rsp.Body.Code = constant.GeneralNoHas
return
}
//是否都有这个武将
gss ,ok := mgr.GMgr.HasGenerals(role.RId,reqObj.GIds)
if ok == false{
rsp.Body.Code = constant.GeneralNoHas
return
}
ok = true
for _, v := range gss {
t := v
if t.CfgId != gs.CfgId {
ok = false
}
}
//是否同一个类型的武将
if ok == false {
rsp.Body.Code = constant.GeneralNoSame
return
}
//是否超过武将星级
if int(gs.Star - gs.StarLv) < len(gss){
rsp.Body.Code = constant.GeneralStarMax
return
}
gs.StarLv += int8(len(gss))
gs.HasPrPoint += static_conf.Basic.General.PrPoint * len(gss)
gs.SyncExecute()
for _, v := range gss {
t := v
t.ParentId = gs.Id
t.State = model.GeneralComposeStar
t.SyncExecute()
}
rspObj.Generals = make([]proto.General, len(gss))
for i, v := range gss {
rspObj.Generals[i] = v.ToProto().(proto.General)
}
rspObj.Generals = append(rspObj.Generals,gs.ToProto().(proto.General))
}
func (this*General) addPrGeneral(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.AddPrGeneralReq{}
rspObj := &proto.AddPrGeneralRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
gs, ok := mgr.GMgr.HasGeneral(role.RId,reqObj.CompId)
//是否有这个武将
if ok == false{
rsp.Body.Code = constant.GeneralNoHas
return
}
all:= reqObj.ForceAdd + reqObj.StrategyAdd + reqObj.DefenseAdd + reqObj.SpeedAdd + reqObj.DestroyAdd
if gs.HasPrPoint < all{
rsp.Body.Code = constant.DBError
return
}
gs.ForceAdded = reqObj.ForceAdd
gs.StrategyAdded = reqObj.StrategyAdd
gs.DefenseAdded = reqObj.DefenseAdd
gs.SpeedAdded = reqObj.SpeedAdd
gs.DestroyAdded = reqObj.DestroyAdd
gs.UsePrPoint = all
gs.SyncExecute()
rsp.Body.Code = constant.OK
rspObj.Generals = gs.ToProto().(proto.General)
}
func (this*General) convert(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ConvertReq{}
rspObj := &proto.ConvertRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
roleRes, ok:= mgr.RResMgr.Get(role.RId)
if ok == false {
rsp.Body.Code = constant.DBError
return
}
gold := 0
okArray := make([]int, 0)
for _, gid := range reqObj.GIds {
g, ok := mgr.GMgr.GetByGId(gid)
if ok && g.Order == 0{
okArray = append(okArray, gid)
gold += 10* int(g.Star)*(1 + int(g.StarLv))
g.State = model.GeneralConvert
g.SyncExecute()
}
}
roleRes.Gold += gold
rspObj.AddGold = gold
rspObj.Gold = roleRes.Gold
rspObj.GIds = okArray
roleRes.SyncExecute()
}
func (this*General) upSkill(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.UpDownSkillReq{}
rspObj := &proto.UpDownSkillRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
rspObj.Pos = reqObj.Pos
rspObj.CfgId = reqObj.CfgId
rspObj.GId = reqObj.GId
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if reqObj.Pos <0 || reqObj.Pos >= model.SkillLimit {
rsp.Body.Code = constant.InvalidParam
return
}
g,ok := mgr.GMgr.GetByGId(reqObj.GId)
if ok == false{
rsp.Body.Code = constant.GeneralNotFound
return
}
if g.RId != role.RId{
rsp.Body.Code = constant.GeneralNotMe
return
}
skill, ok := mgr.SkillMgr.GetSkillOrCreate(role.RId, reqObj.CfgId)
if ok == false {
rsp.Body.Code = constant.DBError
return
}
if skill.IsInLimit() == false{
rsp.Body.Code = constant.OutSkillLimit
return
}
if skill.ArmyIsIn(g.CurArms) == false{
rsp.Body.Code = constant.OutArmNotMatch
return
}
if g.UpSkill(skill.Id, reqObj.CfgId, reqObj.Pos) == false{
rsp.Body.Code = constant.UpSkillError
return
}
skill.UpSkill(g.Id)
g.SyncExecute()
skill.SyncExecute()
}
func (this*General) downSkill(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.UpDownSkillReq{}
rspObj := &proto.UpDownSkillRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
rspObj.Pos = reqObj.Pos
rspObj.CfgId = reqObj.CfgId
rspObj.GId = reqObj.GId
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if reqObj.Pos <0 || reqObj.Pos >= model.SkillLimit {
rsp.Body.Code = constant.InvalidParam
return
}
g,ok := mgr.GMgr.GetByGId(reqObj.GId)
if ok == false{
rsp.Body.Code = constant.GeneralNotFound
return
}
if g.RId != role.RId{
rsp.Body.Code = constant.GeneralNotMe
return
}
skill, ok := mgr.SkillMgr.GetSkillOrCreate(role.RId, reqObj.CfgId)
if ok == false{
rsp.Body.Code = constant.DBError
return
}
if g.DownSkill(skill.Id, reqObj.Pos) == false{
rsp.Body.Code = constant.DownSkillError
return
}
skill.DownSkill(g.Id)
g.SyncExecute()
skill.SyncExecute()
}
func (this*General) lvSkill(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.LvSkillReq{}
rspObj := &proto.LvSkillRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
rspObj.Pos = reqObj.Pos
rspObj.GId = reqObj.GId
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
g, ok := mgr.GMgr.GetByGId(reqObj.GId)
if ok == false{
rsp.Body.Code = constant.GeneralNotFound
return
}
if g.RId != role.RId {
rsp.Body.Code = constant.GeneralNotMe
return
}
gSkill, err := g.PosSkill(reqObj.Pos)
if err != nil{
rsp.Body.Code = constant.PosNotSkill
return
}
skillCfg, ok := skill.Skill.GetCfg(gSkill.CfgId)
if ok == false{
rsp.Body.Code = constant.PosNotSkill
return
}
if gSkill.Lv > len(skillCfg.Levels){
rsp.Body.Code = constant.SkillLevelFull
return
}
gSkill.Lv += 1
g.SyncExecute()
}

View File

@@ -0,0 +1,200 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/proto"
"slgserver/server/slgserver/static_conf"
"slgserver/server/slgserver/static_conf/facility"
"time"
)
var DefaultInterior = Interior{}
type Interior struct {
}
func (this*Interior) InitRouter(r *net.Router) {
g := r.Group("interior").Use(middleware.ElapsedTime(),
middleware.Log(), middleware.CheckRole())
g.AddRouter("collect", this.collect)
g.AddRouter("openCollect", this.openCollect)
g.AddRouter("transform", this.transform)
}
func (this*Interior) collect(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.CollectionReq{}
rspObj := &proto.CollectionRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
roleRes, ok:= mgr.RResMgr.Get(role.RId)
if ok == false {
rsp.Body.Code = constant.DBError
return
}
roleAttr, ok:= mgr.RAttrMgr.Get(role.RId)
if ok == false {
rsp.Body.Code = constant.DBError
return
}
curTime := time.Now()
lastTime := roleAttr.LastCollectTime
if curTime.YearDay() != lastTime.YearDay() || curTime.Year() != lastTime.Year(){
roleAttr.CollectTimes = 0
roleAttr.LastCollectTime = time.Time{}
}
timeLimit := static_conf.Basic.Role.CollectTimesLimit
//是否超过征收次数上限
if roleAttr.CollectTimes >= timeLimit{
rsp.Body.Code = constant.OutCollectTimesLimit
return
}
//cd内不能操作
need := lastTime.Add(time.Duration(static_conf.Basic.Role.CollectTimesLimit)*time.Second)
if curTime.Before(need){
rsp.Body.Code = constant.InCdCanNotOperate
return
}
gold := mgr.GetYield(roleRes.RId).Gold
rspObj.Gold = gold
roleRes.Gold += gold
//更新
roleRes.SyncExecute()
roleAttr.LastCollectTime = curTime
roleAttr.CollectTimes += 1
roleAttr.SyncExecute()
interval := static_conf.Basic.Role.CollectInterval
if roleAttr.CollectTimes >= timeLimit {
y, m, d := roleAttr.LastCollectTime.Add(24*time.Hour).Date()
nextTime := time.Date(y, m, d, 0, 0, 0, 0, time.FixedZone("IST", 3600))
rspObj.NextTime = nextTime.UnixNano()/1e6
}else{
nextTime := roleAttr.LastCollectTime.Add(time.Duration(interval)*time.Second)
rspObj.NextTime = nextTime.UnixNano()/1e6
}
rspObj.CurTimes = roleAttr.CollectTimes
rspObj.Limit = timeLimit
}
func (this*Interior) openCollect(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.OpenCollectionReq{}
rspObj := &proto.OpenCollectionRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
roleAttr, ok:= mgr.RAttrMgr.Get(role.RId)
if ok == false {
rsp.Body.Code = constant.DBError
return
}
interval := static_conf.Basic.Role.CollectInterval
timeLimit := static_conf.Basic.Role.CollectTimesLimit
rspObj.Limit = timeLimit
rspObj.CurTimes = roleAttr.CollectTimes
if roleAttr.LastCollectTime.IsZero() {
rspObj.NextTime = 0
}else{
if roleAttr.CollectTimes >= timeLimit {
y, m, d := roleAttr.LastCollectTime.Add(24*time.Hour).Date()
nextTime := time.Date(y, m, d, 0, 0, 0, 0, time.FixedZone("IST", 3600))
rspObj.NextTime = nextTime.UnixNano()/1e6
}else{
nextTime := roleAttr.LastCollectTime.Add(time.Duration(interval)*time.Second)
rspObj.NextTime = nextTime.UnixNano()/1e6
}
}
}
func (this*Interior) transform(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.TransformReq{}
rspObj := &proto.TransformRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
roleRes, ok:= mgr.RResMgr.Get(role.RId)
if ok == false {
rsp.Body.Code = constant.DBError
return
}
main, _ := mgr.RCMgr.GetMainCity(role.RId)
lv := mgr.RFMgr.GetFacilityLv(main.CityId, facility.JiShi)
if lv <= 0{
rsp.Body.Code = constant.NotHasJiShi
return
}
len := 4
ret := make([]int, len)
for i := 0 ;i < len; i++{
//ret[i] = reqObj.To[i] - reqObj.From[i]
if reqObj.From[i] > 0{
ret[i] = -reqObj.From[i]
}
if reqObj.To[i] > 0{
ret[i] = reqObj.To[i]
}
}
if roleRes.Wood + ret[0] < 0{
rsp.Body.Code = constant.InvalidParam
return
}
if roleRes.Iron + ret[1] < 0{
rsp.Body.Code = constant.InvalidParam
return
}
if roleRes.Stone + ret[2] < 0{
rsp.Body.Code = constant.InvalidParam
return
}
if roleRes.Grain + ret[3] < 0{
rsp.Body.Code = constant.InvalidParam
return
}
roleRes.Wood += ret[0]
roleRes.Iron += ret[1]
roleRes.Stone += ret[2]
roleRes.Grain += ret[3]
roleRes.SyncExecute()
}

View File

@@ -0,0 +1,274 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/logic"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/proto"
"slgserver/server/slgserver/static_conf"
)
var DefaultMap = NationMap{}
type NationMap struct {
}
func (this*NationMap) InitRouter(r *net.Router) {
g := r.Group("nationMap").Use(middleware.ElapsedTime(), middleware.Log())
g.AddRouter("config", this.config)
g.AddRouter("scan", this.scan, middleware.CheckRole())
g.AddRouter("scanBlock", this.scanBlock, middleware.CheckRole())
g.AddRouter("giveUp", this.giveUp, middleware.CheckRole())
g.AddRouter("build", this.build, middleware.CheckRole())
g.AddRouter("upBuild", this.upBuild, middleware.CheckRole())
g.AddRouter("delBuild", this.delBuild, middleware.CheckRole())
}
/*
获取配置
*/
func (this*NationMap) config(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ConfigReq{}
rspObj := &proto.ConfigRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
m := static_conf.MapBuildConf.Cfg
rspObj.Confs = make([]proto.Conf, len(m))
i := 0
for _, v := range m {
rspObj.Confs[i].Type = v.Type
rspObj.Confs[i].Name = v.Name
rspObj.Confs[i].Level = v.Level
rspObj.Confs[i].Defender = v.Defender
rspObj.Confs[i].Durable = v.Durable
rspObj.Confs[i].Grain = v.Grain
rspObj.Confs[i].Iron = v.Iron
rspObj.Confs[i].Stone = v.Stone
rspObj.Confs[i].Wood = v.Wood
i++
}
}
/*
扫描地图
*/
func (this*NationMap) scan(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ScanReq{}
rspObj := &proto.ScanRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
x := reqObj.X
y := reqObj.Y
rb := mgr.RBMgr.Scan(x, y)
rspObj.MRBuilds = make([]proto.MapRoleBuild, len(rb))
for i, v := range rb {
rspObj.MRBuilds[i] = v.ToProto().(proto.MapRoleBuild)
}
cb := mgr.RCMgr.Scan(x, y)
rspObj.MCBuilds = make([]proto.MapRoleCity, len(cb))
for i, v := range cb {
rspObj.MCBuilds[i] = v.ToProto().(proto.MapRoleCity)
}
}
func (this*NationMap) scanBlock(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.ScanBlockReq{}
rspObj := &proto.ScanRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
x := reqObj.X
y := reqObj.Y
rb := mgr.RBMgr.ScanBlock(x, y, reqObj.Length)
rspObj.MRBuilds = make([]proto.MapRoleBuild, len(rb))
for i, v := range rb {
rspObj.MRBuilds[i] = v.ToProto().(proto.MapRoleBuild)
}
cb := mgr.RCMgr.ScanBlock(x, y, reqObj.Length)
rspObj.MCBuilds = make([]proto.MapRoleCity, len(cb))
for i, v := range cb {
rspObj.MCBuilds[i] = v.ToProto().(proto.MapRoleCity)
}
armys := logic.ArmyLogic.ScanBlock(role.RId, x, y, reqObj.Length)
rspObj.Armys = make([]proto.Army, len(armys))
for i, v := range armys {
rspObj.Armys[i] = v.ToProto().(proto.Army)
}
}
func (this*NationMap) giveUp(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.GiveUpReq{}
rspObj := &proto.GiveUpRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
x := reqObj.X
y := reqObj.Y
rspObj.X = x
rspObj.Y = y
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if mgr.RBMgr.BuildIsRId(x, y, role.RId) == false{
rsp.Body.Code = constant.BuildNotMe
return
}
rsp.Body.Code = mgr.RBMgr.GiveUp(x, y)
}
//建造
func (this*NationMap) build(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.BuildReq{}
rspObj := &proto.BuildRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
x := reqObj.X
y := reqObj.Y
rspObj.X = x
rspObj.Y = y
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if mgr.RBMgr.BuildIsRId(x, y, role.RId) == false{
rsp.Body.Code = constant.BuildNotMe
return
}
b, ok := mgr.RBMgr.PositionBuild(x, y)
if ok == false {
rsp.Body.Code = constant.BuildNotMe
return
}
if b.IsResBuild() == false || b.IsBusy(){
rsp.Body.Code = constant.CanNotBuildNew
return
}
cnt := mgr.RBMgr.RoleFortressCnt(role.RId)
if cnt >= static_conf.Basic.Build.FortressLimit{
rsp.Body.Code = constant.CanNotBuildNew
return
}
cfg, ok := static_conf.MapBCConf.BuildConfig(reqObj.Type, 1)
if ok == false{
rsp.Body.Code = constant.InvalidParam
return
}
code := mgr.RResMgr.TryUseNeed(role.RId, cfg.Need)
if code != constant.OK {
rsp.Body.Code = code
return
}
b.BuildOrUp(*cfg)
b.SyncExecute()
}
func (this*NationMap) upBuild(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.UpBuildReq{}
rspObj := &proto.UpBuildRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
x := reqObj.X
y := reqObj.Y
rspObj.X = x
rspObj.Y = y
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if mgr.RBMgr.BuildIsRId(x, y, role.RId) == false{
rsp.Body.Code = constant.BuildNotMe
return
}
b, ok := mgr.RBMgr.PositionBuild(x, y)
if ok == false {
rsp.Body.Code = constant.BuildNotMe
return
}
if b.IsHaveModifyLVAuth() == false || b.IsInGiveUp() || b.IsBusy(){
rsp.Body.Code = constant.CanNotUpBuild
return
}
cfg, ok := static_conf.MapBCConf.BuildConfig(b.Type, b.Level+1)
if ok == false{
rsp.Body.Code = constant.InvalidParam
return
}
code := mgr.RResMgr.TryUseNeed(role.RId, cfg.Need)
if code != constant.OK {
rsp.Body.Code = code
return
}
b.BuildOrUp(*cfg)
b.SyncExecute()
rspObj.Build = b.ToProto().(proto.MapRoleBuild)
}
func (this*NationMap) delBuild(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.UpBuildReq{}
rspObj := &proto.UpBuildRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
x := reqObj.X
y := reqObj.Y
rspObj.X = x
rspObj.Y = y
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
if mgr.RBMgr.BuildIsRId(x, y, role.RId) == false{
rsp.Body.Code = constant.BuildNotMe
return
}
rsp.Body.Code = mgr.RBMgr.Destroy(x, y)
b, ok := mgr.RBMgr.PositionBuild(x, y)
if ok {
rspObj.Build = b.ToProto().(proto.MapRoleBuild)
}
}

View File

@@ -0,0 +1,412 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"log/slog"
"math/rand"
"slgserver/constant"
"slgserver/db"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/global"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/pos"
"slgserver/server/slgserver/proto"
"slgserver/server/slgserver/static_conf"
"slgserver/util"
"time"
)
var DefaultRole = Role{}
type Role struct {
}
func (this*Role) InitRouter(r *net.Router) {
g := r.Group("role").Use(middleware.ElapsedTime(), middleware.Log())
g.AddRouter("enterServer", this.enterServer)
g.AddRouter("create", this.create, middleware.CheckLogin())
g.AddRouter("roleList", this.roleList, middleware.CheckLogin())
g.AddRouter("myCity", this.myCity, middleware.CheckRole())
g.AddRouter("myRoleRes", this.myRoleRes, middleware.CheckRole())
g.AddRouter("myRoleBuild", this.myRoleBuild, middleware.CheckRole())
g.AddRouter("myProperty", this.myProperty, middleware.CheckRole())
g.AddRouter("upPosition", this.upPosition, middleware.CheckRole())
g.AddRouter("posTagList", this.posTagList, middleware.CheckRole())
g.AddRouter("opPosTag", this.opPosTag, middleware.CheckRole())
}
func (this*Role) create(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.CreateRoleReq{}
rspObj := &proto.CreateRoleRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
uid, _ := req.Conn.GetProperty("uid")
reqObj.UId = uid.(int)
rspObj.Role.UId = reqObj.UId
r := make([]model.Role, 0)
has, _ := db.MasterDB.Table(r).Where("uid=?", reqObj.UId).Get(r)
if has {
slog.Info("role has create", "uid", reqObj.UId)
rsp.Body.Code = constant.RoleAlreadyCreate
}else {
role := &model.Role{UId: reqObj.UId, HeadId: reqObj.HeadId, Sex: reqObj.Sex,
NickName: reqObj.NickName, CreatedAt: time.Now()}
if _, err := db.MasterDB.Insert(role); err != nil {
slog.Info("role create error",
"uid", reqObj.UId, "error", err)
rsp.Body.Code = constant.DBError
}else{
rspObj.Role.RId = role.RId
rspObj.Role.UId = reqObj.UId
rspObj.Role.NickName = reqObj.NickName
rspObj.Role.Sex = reqObj.Sex
rspObj.Role.HeadId = reqObj.HeadId
rsp.Body.Code = constant.OK
}
}
}
func (this*Role) roleList(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.RoleListReq{}
rspObj := &proto.RoleListRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
uid, _ := req.Conn.GetProperty("uid")
uid = uid.(int)
r := make([]*model.Role, 0)
err := db.MasterDB.Table(r).Where("uid=?", uid).Find(&r)
if err == nil{
rl := make([]proto.Role, len(r))
for i, v := range r {
rl[i] = v.ToProto().(proto.Role)
}
rspObj.Roles = rl
rsp.Body.Code = constant.OK
}else{
rsp.Body.Code = constant.DBError
}
}
func (this*Role) enterServer(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.EnterServerReq{}
rspObj := &proto.EnterServerRsp{}
rspObj.Time = time.Now().UnixNano()/1e6
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
//校验session是否合法
sess, err := util.ParseSession(reqObj.Session)
if err != nil || sess.IsValid() == false{
rsp.Body.Code = constant.SessionInvalid
return
}
uid := sess.Id
req.Conn.SetProperty("uid", uid)
//查询角色
role := &model.Role{}
b, err := db.MasterDB.Table(role).Where("uid=?", uid).Get(role)
if err != nil{
slog.Warn("enterServer db error", "error", err)
rsp.Body.Code = constant.DBError
return
}
if b {
rsp.Body.Code = constant.OK
rspObj.Role = role.ToProto().(proto.Role)
req.Conn.SetProperty("role", role)
net.ConnMgr.RoleEnter(req.Conn, role.RId)
var e error = nil
roleRes, ok := mgr.RResMgr.Get(role.RId)
if ok == false{
roleRes = &model.RoleRes{RId: role.RId,
Wood: static_conf.Basic.Role.Wood,
Iron: static_conf.Basic.Role.Iron,
Stone: static_conf.Basic.Role.Stone,
Grain: static_conf.Basic.Role.Grain,
Gold: static_conf.Basic.Role.Gold,
Decree: static_conf.Basic.Role.Decree}
_ ,e = db.MasterDB.Insert(roleRes)
if e != nil {
slog.Error("insert rres error", "error", e)
}
}
if e == nil {
mgr.RResMgr.Add(roleRes)
rspObj.RoleRes = roleRes.ToProto().(proto.RoleRes)
rsp.Body.Code = constant.OK
}else{
rsp.Body.Code = constant.DBError
return
}
//玩家的一些属性
if _, ok := mgr.RAttrMgr.TryCreate(role.RId); ok == false{
rsp.Body.Code = constant.DBError
return
}
//查询是否有城市
_, ok = mgr.RCMgr.GetByRId(role.RId)
if ok == false{
citys := make([]*model.MapRoleCity, 0)
//随机生成一个城市
for true {
x := rand.Intn(global.MapWith)
y := rand.Intn(global.MapHeight)
if mgr.NMMgr.IsCanBuildCity(x, y){
//建立城市
c := &model.MapRoleCity{RId: role.RId, X: x, Y: y,
IsMain: 1,
CurDurable: static_conf.Basic.City.Durable,
Name: role.NickName,
CreatedAt: time.Now(),
}
//插入
_, err := db.MasterDB.Table(c).Insert(c)
if err != nil{
rsp.Body.Code = constant.DBError
}else{
citys = append(citys, c)
//更新城市缓存
mgr.RCMgr.Add(c)
}
//生成城市里面的设施
mgr.RFMgr.GetAndTryCreate(c.CityId, c.RId)
break
}
}
}
rspObj.Token = util.NewSession(role.RId, time.Now()).String()
}else{
rsp.Body.Code = constant.RoleNotExist
}
}
func (this*Role) myCity(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.MyCityReq{}
rspObj := &proto.MyCityRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role, _ := r.(*model.Role)
citys,ok := mgr.RCMgr.GetByRId(role.RId)
if ok {
rspObj.Citys = make([]proto.MapRoleCity, len(citys))
//赋值发送
for i, v := range citys {
rspObj.Citys[i] = v.ToProto().(proto.MapRoleCity)
}
}else{
rspObj.Citys = make([]proto.MapRoleCity, 0)
}
}
func (this*Role) myRoleRes(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.MyRoleResReq{}
rspObj := &proto.MyRoleResRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
roleRes, ok := mgr.RResMgr.Get(role.RId)
if ok == false{
rsp.Body.Code = constant.RoleNotExist
return
}else{
rspObj.RoleRes = roleRes.ToProto().(proto.RoleRes)
rsp.Body.Code = constant.OK
}
}
func (this*Role) myProperty(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.MyRolePropertyReq{}
rspObj := &proto.MyRolePropertyRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
//城市
c, ok := mgr.RCMgr.GetByRId(role.RId)
if ok {
rspObj.Citys = make([]proto.MapRoleCity, len(c))
for i, v := range c {
rspObj.Citys[i] = v.ToProto().(proto.MapRoleCity)
}
}else{
rspObj.Citys = make([]proto.MapRoleCity, 0)
}
//建筑
ra, ok := mgr.RBMgr.GetRoleBuild(role.RId)
if ok {
rspObj.MRBuilds = make([]proto.MapRoleBuild, len(ra))
for i, v := range ra {
rspObj.MRBuilds[i] = v.ToProto().(proto.MapRoleBuild)
}
}else{
rspObj.MRBuilds = make([]proto.MapRoleBuild, 0)
}
//资源
roleRes, ok := mgr.RResMgr.Get(role.RId)
if ok {
rspObj.RoleRes = roleRes.ToProto().(proto.RoleRes)
}else{
rsp.Body.Code = constant.RoleNotExist
return
}
//武将
gs, ok := mgr.GMgr.GetOrCreateByRId(role.RId)
if ok {
rspObj.Generals = make([]proto.General, 0)
for _, v := range gs {
rspObj.Generals = append(rspObj.Generals, v.ToProto().(proto.General))
}
}else{
rsp.Body.Code = constant.DBError
return
}
//军队
ar, ok := mgr.AMgr.GetByRId(role.RId)
if ok {
rspObj.Armys = make([]proto.Army, len(ar))
for i, v := range ar {
rspObj.Armys[i] = v.ToProto().(proto.Army)
}
}else{
rspObj.Armys = make([]proto.Army, 0)
}
}
func (this*Role) myRoleBuild(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.MyRoleBuildReq{}
rspObj := &proto.MyRoleBuildRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
ra, ok := mgr.RBMgr.GetRoleBuild(role.RId)
if ok {
rspObj.MRBuilds = make([]proto.MapRoleBuild, len(ra))
for i, v := range ra {
rspObj.MRBuilds[i] = v.ToProto().(proto.MapRoleBuild)
}
}else{
rspObj.MRBuilds = make([]proto.MapRoleBuild, 0)
}
}
func (this*Role) upPosition(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.UpPositionReq{}
rspObj := &proto.UpPositionRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
rspObj.X = reqObj.X
rspObj.Y = reqObj.Y
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
pos.RPMgr.Push(reqObj.X, reqObj.Y, role.RId)
}
func (this*Role) posTagList(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.PosTagListReq{}
rspObj := &proto.PosTagListRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
attr, ok := mgr.RAttrMgr.Get(role.RId)
if ok == false {
rsp.Body.Code = constant.RoleNotExist
return
}
rspObj.PosTags = attr.PosTagArray
}
func (this*Role) opPosTag(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.PosTagReq{}
rspObj := &proto.PosTagRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
rspObj.X = reqObj.X
rspObj.Y = reqObj.Y
rspObj.Type = reqObj.Type
rspObj.Name = reqObj.Name
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
attr, ok := mgr.RAttrMgr.Get(role.RId)
if ok == false {
rsp.Body.Code = constant.RoleNotExist
return
}
if reqObj.Type == 0{
attr.RemovePosTag(reqObj.X, reqObj.Y)
attr.SyncExecute()
}else if reqObj.Type == 1{
limit := static_conf.Basic.Role.PosTagLimit
if int(limit) >= len(attr.PosTagArray){
attr.AddPosTag(reqObj.X, reqObj.Y, reqObj.Name)
attr.SyncExecute()
}else{
rsp.Body.Code = constant.OutPosTagLimit
}
}else{
rsp.Body.Code = constant.InvalidParam
}
}

View File

@@ -0,0 +1,42 @@
package controller
import (
"github.com/mitchellh/mapstructure"
"slgserver/constant"
"slgserver/middleware"
"slgserver/net"
"slgserver/server/slgserver/logic/mgr"
"slgserver/server/slgserver/model"
"slgserver/server/slgserver/proto"
)
var DefaultSkill = Skill{
}
type Skill struct {
}
func (this*Skill) InitRouter(r *net.Router) {
g := r.Group("skill").Use(middleware.ElapsedTime(), middleware.Log(),
middleware.CheckLogin(), middleware.CheckRole())
g.AddRouter("list", this.list)
}
func (this*Skill) list(req *net.WsMsgReq, rsp *net.WsMsgRsp) {
reqObj := &proto.SkillListReq{}
rspObj := &proto.SkillListRsp{}
mapstructure.Decode(req.Body.Msg, reqObj)
rsp.Body.Msg = rspObj
rsp.Body.Code = constant.OK
r, _ := req.Conn.GetProperty("role")
role := r.(*model.Role)
rspObj.List = make([]proto.Skill, 0)
skills, _ := mgr.SkillMgr.Get(role.RId)
for _, skill := range skills {
rspObj.List = append(rspObj.List, skill.ToProto().(proto.Skill))
}
}

Some files were not shown because too many files have changed in this diff Show More