├── scripts
├── server
│ ├── msgs
│ │ ├── msg_gate.lua
│ │ ├── msg_logic.lua
│ │ └── msg_server.lua
│ ├── daemons
│ │ ├── poker
│ │ │ └── poker_d.lua
│ │ ├── containerd.lua
│ │ ├── package_statd.lua
│ │ ├── chatd.lua
│ │ ├── user_redisd.lua
│ │ ├── redis_scriptd.lua
│ │ ├── data_userd.lua
│ │ ├── cached.lua
│ │ ├── user_dbd.lua
│ │ ├── internal_commd.lua
│ │ ├── attribd.lua
│ │ ├── redis_queued.lua
│ │ └── login_queued.lua
│ ├── redis_scripts
│ │ └── gate_select.lua
│ ├── clone
│ │ ├── rooms
│ │ │ ├── ddz_room.lua
│ │ │ └── desk.lua
│ │ ├── account.lua
│ │ └── ddz_info.lua
│ ├── cmds
│ │ ├── cmd_server.lua
│ │ ├── cmd_gate.lua
│ │ └── cmd_logic.lua
│ └── server.lua
├── data
│ └── txt
│ │ ├── banned_word.txt
│ │ ├── attrib_max.txt
│ │ ├── room.txt
│ │ ├── EquipInfo.txt
│ │ ├── ItemInfo.txt
│ │ ├── exp_user.txt
│ │ ├── attrib_formula.txt
│ │ └── appearance.txt
├── define
│ ├── base.lua
│ └── server_msg.lua
├── global
│ ├── include
│ │ ├── sf_event.lua
│ │ ├── log_define.lua
│ │ └── define.lua
│ ├── base
│ │ ├── global_cookie.lua
│ │ ├── global_basic.lua
│ │ ├── heartbeat.lua
│ │ ├── global_data_boot.lua
│ │ ├── rid.lua
│ │ ├── global_boot.lua
│ │ ├── string_util.lua
│ │ ├── load_folder.lua
│ │ ├── game_util.lua
│ │ ├── log.lua
│ │ ├── global_frame.lua
│ │ ├── raiser.lua
│ │ └── global_agents.lua
│ ├── daemons
│ │ ├── logd.lua
│ │ ├── exitd.lua
│ │ ├── forbidden_wordd.lua
│ │ ├── systemd.lua
│ │ ├── base64d.lua
│ │ └── propertyd.lua
│ ├── inherit
│ │ ├── rid.lua
│ │ ├── property.lua
│ │ ├── attrib.lua
│ │ └── heartbeat.lua
│ └── clone
│ │ ├── queue.lua
│ │ ├── equip.lua
│ │ └── item.lua
├── etc
│ └── format.lua
├── client
│ ├── global
│ │ └── funcitons.lua
│ ├── daemons
│ │ ├── stress_test
│ │ │ └── chat_testd.lua
│ │ ├── logind.lua
│ │ └── med.lua
│ ├── client.lua
│ ├── clone
│ │ └── player.lua
│ ├── msgs
│ │ └── msg_client.lua
│ └── command.lua
├── test
│ └── fix.lua
└── share
│ └── net.lua
├── src
├── game
│ └── mod.rs
├── db
│ ├── mod.rs
│ └── db_trait.rs
├── mgr
│ ├── mod.rs
│ ├── command_mgr.rs
│ ├── tcp_mgr.rs
│ └── protocol_mgr.rs
├── macros.rs
├── utils
│ ├── mod.rs
│ ├── lua_utils.rs
│ ├── time_utils.rs
│ ├── thread_utils.rs
│ ├── file_utils.rs
│ └── net_utils.rs
├── lua_custom
│ ├── lua_timer.rs
│ └── mod.rs
├── protocol
│ ├── mod.rs
│ ├── proto_bin.rs
│ ├── proto_text.rs
│ ├── proto_json.rs
│ └── proto_rt.rs
├── net
│ └── mod.rs
├── lib.rs
├── global_config.rs
├── rp_wrapper.rs
└── redis_wrapper.rs
├── doc
├── img
│ └── network_structure.png
└── 游戏大厅大纲.md
├── .travis.yml
├── .gitignore
├── .project
├── config
├── log4rs.yml
├── log4rs_client.yml
├── gate.yaml
├── client.yaml
└── server.yaml
├── LICENSE-MIT
├── Cargo.toml
├── README.md
├── examples
├── client.rs
└── server.rs
├── .gitattributes
└── .vscode
└── launch.json
/scripts/server/msgs/msg_gate.lua:
--------------------------------------------------------------------------------
1 | --msg_gate.lua
--------------------------------------------------------------------------------
/scripts/server/msgs/msg_logic.lua:
--------------------------------------------------------------------------------
1 | --msg_logic.lua
--------------------------------------------------------------------------------
/scripts/server/msgs/msg_server.lua:
--------------------------------------------------------------------------------
1 | --msg_server.lua
--------------------------------------------------------------------------------
/scripts/data/txt/banned_word.txt:
--------------------------------------------------------------------------------
1 | 系统
2 | 草榴
3 | 裸聊
4 | 重口味
--------------------------------------------------------------------------------
/scripts/define/base.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | MSG_FLAG_NORMAL = 0
4 | MSG_FLAG_FORWARD = 1
--------------------------------------------------------------------------------
/src/game/mod.rs:
--------------------------------------------------------------------------------
1 |
2 | pub mod majiang;
3 |
4 | pub use self::majiang::{MaJiang, KindItem};
5 |
--------------------------------------------------------------------------------
/doc/img/network_structure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tickbh/tunm/HEAD/doc/img/network_structure.png
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: rust
2 | rust:
3 | - beta
4 | - stable
5 | script:
6 | - cargo build --verbose
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 | /td_clua_ext/target
4 | /td_clua_ext/Cargo.lock
5 | /log
6 | /*.tags
7 | /*.tags_sorted_by_file
8 | db
9 | /websocket/target
--------------------------------------------------------------------------------
/scripts/global/include/sf_event.lua:
--------------------------------------------------------------------------------
1 | -- sf_event.lua
2 | -- Created by wugd
3 | -- 定义事件
4 |
5 | -- 伙伴等级提升
6 | SF_LEVEL_UP = "SF_LEVEL_UP";
7 |
--------------------------------------------------------------------------------
/scripts/data/txt/attrib_max.txt:
--------------------------------------------------------------------------------
1 | #属性上限表
2 | #属性名 上限值 类型
3 | string int string
4 | name max ob_type
5 | exp 1000000 OB_TYPE_USER
6 | gold 100000000 OB_TYPE_USER
7 | stone 100000000 OB_TYPE_USER
8 | lv 50 OB_TYPE_USER
9 | vip 15 OB_TYPE_USER
10 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | tunm
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/scripts/data/txt/room.txt:
--------------------------------------------------------------------------------
1 | #场景编号,以数字为编号* 场景类名 游戏类型(确定根据游戏类型获取相关的数据) 场景名称* 地图未开放,0开放 1不开放 若是VIP场景则填1,不是填0 桌子数量
2 | string string string string int int int
3 | room_name room_tdcls game_type name is_unopen is_vip desk_num
4 | ddz1 DDZ_ROOM_TDCLS ddz 斗地主1 1 0 100
5 | ddz2 DDZ_ROOM_TDCLS ddz 斗地主2 1 0 100
6 |
--------------------------------------------------------------------------------
/scripts/data/txt/EquipInfo.txt:
--------------------------------------------------------------------------------
1 | #物品表配置信息
2 | #装备id 装备图标 装备名字 物品类型(2物品,3装备) 物品介绍 装备品质(1白、2绿、3蓝、4紫、5橙、6橙1 、7橙2、 8橙3) 装备部位(1、武器;2、衣服;3、手套;4、鞋子;5、项链;6、戒指) 出售价格(金币) 最大叠加数
3 | int string string int string int int float int
4 | class_id icon name ob_type describe quality equip_position sell_price over_lap
5 | 10001 wuqi1-icon 这是一把剑 3 这是一把普通的剑 1 1 100 1
6 |
--------------------------------------------------------------------------------
/src/db/mod.rs:
--------------------------------------------------------------------------------
1 |
2 | pub mod db_trait;
3 | pub mod db_mysql;
4 | pub mod redis_pool;
5 | pub mod db_sqlite;
6 | pub mod db_pool;
7 |
8 | pub use self::db_trait::DbTrait;
9 | pub use self::db_mysql::DbMysql;
10 | pub use self::db_sqlite::DbSqlite;
11 | pub use self::db_pool::{DbPool, PoolTrait, DbStruct};
12 | pub use self::redis_pool::RedisPool;
--------------------------------------------------------------------------------
/doc/游戏大厅大纲.md:
--------------------------------------------------------------------------------
1 | 游戏大厅
2 | 由网关服(可多个),大厅服(可多个),和游戏逻辑服组成,其中游戏逻辑服可布置在大厅服上
3 |
4 | 游戏逻辑服与大厅服的交互,主要通过redis的订阅发布进行交互,
5 | 客户端发送消息给网关服,网关服把消息转发给大厅服,大厅服把消息发送给对应的游戏逻辑服
6 | 游戏逻辑服解析消息,进行处理游戏逻辑,并通过发布消息把消息返回给大厅,然后大厅转发给对应的网关服,再传回给客户端
7 |
8 | 游戏逻辑服会定期把对应的房间信息汇报给大厅服,假设定时每60秒发布一次,则可能设置180秒为过期时间,180秒后没有再收到游戏逻辑服的消息,则此房间处于异常状态
9 | 只有状态正常的服务器接收玩家的接入。
10 |
11 | 游戏服里有多个房间,不同的房间维持不同的逻辑状态,如斗地主房间,升级房间。
12 |
13 |
--------------------------------------------------------------------------------
/src/mgr/mod.rs:
--------------------------------------------------------------------------------
1 |
2 | mod http_mgr;
3 | mod command_mgr;
4 | mod mio_event_mgr;
5 | mod protocol_mgr;
6 | mod websocket_mgr;
7 | mod tcp_mgr;
8 |
9 | pub use self::http_mgr::HttpMgr;
10 | pub use self::command_mgr::CommandMgr;
11 | pub use self::mio_event_mgr::MioEventMgr;
12 | pub use self::protocol_mgr::ProtocolMgr;
13 | pub use self::websocket_mgr::{WebSocketMgr, WebsocketClient};
14 | pub use self::tcp_mgr::TcpMgr;
--------------------------------------------------------------------------------
/scripts/etc/format.lua:
--------------------------------------------------------------------------------
1 | --format目录,实际项目可移至策划目录,让策划进行维护
2 |
3 | function GET_GOLD_ID()
4 | return 101
5 | end
6 |
7 | function GET_STONE_ID()
8 | return 102
9 | end
10 |
11 | function CALC_ITEM_MAX_AMOUNT(ob)
12 | if IS_OBJECT(ob) then
13 | return ob:query("over_lap") or 1
14 | elseif IS_TABLE(ob) then
15 | return ob["over_lap"] or 1
16 | else
17 | return 1
18 | end
19 | end
--------------------------------------------------------------------------------
/src/macros.rs:
--------------------------------------------------------------------------------
1 | #![macro_use]
2 |
3 | macro_rules! fail {
4 | ($expr:expr) => (
5 | return Err(::std::convert::From::from($expr))
6 | )
7 | }
8 |
9 | macro_rules! unwrap_or {
10 | ($expr:expr, $or:expr) => (
11 | match $expr {
12 | Some(x) => x,
13 | None => { $or }
14 | }
15 | )
16 | }
17 |
18 | #[macro_export]
19 | macro_rules! raw_to_ref {
20 | ($expr:expr) => (unsafe { &mut *$expr})
21 | }
22 |
--------------------------------------------------------------------------------
/src/utils/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod file_utils;
2 | pub mod time_utils;
3 | pub mod thread_utils;
4 | pub mod net_utils;
5 | pub mod telnet_utils;
6 | pub mod log_utils;
7 | pub mod lua_utils;
8 |
9 | pub use self::file_utils::FileUtils;
10 | pub use self::time_utils::TimeUtils;
11 | pub use self::thread_utils::ThreadUtils;
12 | pub use self::net_utils::NetUtils;
13 | pub use self::telnet_utils::TelnetUtils;
14 | pub use self::log_utils::LogUtils;
15 | pub use self::lua_utils::LuaUtils;
16 |
--------------------------------------------------------------------------------
/scripts/data/txt/ItemInfo.txt:
--------------------------------------------------------------------------------
1 | #物品表配置信息
2 | #物品id 物品图标 物品名字 物品类型名称 物品介绍 物品类型(2物品,3装备) 是否能合并 限制等级(0表示不限制等级) 物品品质(1白、2绿、3蓝、4紫、5橙、6橙1、 7橙2、 8橙3) 出售价格(金币) 最大叠加数 是否可使用(1、可使用;0、不可使用)
3 | int string string string string int int int int float int int
4 | class_id icon name class_name describe ob_type combine level quality sell_price over_lap if_use
5 | 1 test 测试物品 这是一个测试物品 不可使用 2 1 0 1 10 999 0
6 | 101 jinbi1 1金币 金币 可获得1金币 2 1 0 5 0 999 0
7 | 102 zuanshi1 1钻石 钻石 使用获得1钻石 2 1 0 5 0 999 0
8 | 103 tili1 1体力 体力丹 使用获得1点体力 2 1 0 3 0 999 1
9 |
--------------------------------------------------------------------------------
/scripts/global/base/global_cookie.lua:
--------------------------------------------------------------------------------
1 | -- global_cookie.lua
2 | -- 生成 cookie 的工具函数
3 |
4 | local s_cookie = 0;
5 | local i_cookie = 0;
6 |
7 | -- 取得新的 cookie
8 | function new_cookie()
9 | s_cookie = s_cookie + 1;
10 | s_cookie = bit32.band(s_cookie, 0xffffffff);
11 | s_cookie = (s_cookie == 0 and 1 or s_cookie);
12 |
13 | return s_cookie;
14 | end
15 |
16 | function new_int_cookie()
17 | i_cookie = i_cookie + 1;
18 | i_cookie = bit32.band(i_cookie, 0x0fffffff);
19 | i_cookie = (i_cookie == 0 and 1 or i_cookie);
20 | return i_cookie;
21 | end
22 |
23 |
--------------------------------------------------------------------------------
/src/lua_custom/lua_timer.rs:
--------------------------------------------------------------------------------
1 | use td_rlua::{self, Lua};
2 | use crate::{MioEventMgr};
3 |
4 | fn timer_event_del(time: u32) -> u32 {
5 | MioEventMgr::instance().delete_timer(time as u64);
6 | 0
7 | }
8 |
9 | fn timer_event_set(time: u32, repeat: bool, at_once: bool) -> u32 {
10 | MioEventMgr::instance().add_timer_step("lua_set".to_string(), time as u64, repeat, at_once) as u32
11 | }
12 |
13 | pub fn register_timer_func(lua: &mut Lua) {
14 | lua.set("timer_event_del", td_rlua::function1(timer_event_del));
15 | lua.set("timer_event_set", td_rlua::function3(timer_event_set));
16 | }
17 |
--------------------------------------------------------------------------------
/config/log4rs.yml:
--------------------------------------------------------------------------------
1 | refresh_rate: 30 seconds
2 |
3 | appenders:
4 | stdout:
5 | kind: console
6 |
7 | default:
8 | kind: file
9 | path: "log/default.log"
10 | encoder:
11 | pattern: "{d} - {m}{n}"
12 |
13 |
14 | root:
15 | level: info
16 | appenders:
17 | - default
18 | - stdout
19 |
20 | loggers:
21 | app::backend::db:
22 | level: trace
23 |
24 | tunm:
25 | level: trace
26 | appenders:
27 | - default
28 | - stdout
29 | additive: false
30 |
31 | websocket:
32 | level: info
33 | appenders:
34 | - default
35 | additive: false
--------------------------------------------------------------------------------
/config/log4rs_client.yml:
--------------------------------------------------------------------------------
1 | refresh_rate: 30 seconds
2 |
3 | appenders:
4 | stdout:
5 | kind: console
6 |
7 | default:
8 | kind: file
9 | path: "log/client.log"
10 | encoder:
11 | pattern: "{d} - {m}{n}"
12 |
13 |
14 | root:
15 | level: info
16 | appenders:
17 | - default
18 | - stdout
19 |
20 | loggers:
21 | app::backend::db:
22 | level: trace
23 |
24 | tunm:
25 | level: trace
26 | appenders:
27 | - default
28 | - stdout
29 | additive: false
30 |
31 | websocket:
32 | level: info
33 | appenders:
34 | - default
35 | additive: false
36 |
37 |
--------------------------------------------------------------------------------
/src/protocol/mod.rs:
--------------------------------------------------------------------------------
1 |
2 | use NetMsg;
3 | use td_rlua::{self};
4 | use {NetResult};
5 |
6 | pub trait EngineProtocol: Sized {
7 | fn pack_protocol(lua: *mut td_rlua::lua_State, index: i32) -> Option;
8 | fn unpack_protocol(lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult;
9 | fn convert_string(lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult;
10 | }
11 |
12 | mod proto_rt;
13 | mod proto_json;
14 | mod proto_bin;
15 | mod proto_text;
16 |
17 | pub use self::proto_rt::ProtoRt;
18 | pub use self::proto_json::ProtoJson;
19 | pub use self::proto_bin::ProtoBin;
20 | pub use self::proto_text::ProtoText;
21 |
--------------------------------------------------------------------------------
/scripts/client/global/funcitons.lua:
--------------------------------------------------------------------------------
1 | HEARTBEAT_INTERVAL = 10000
2 |
3 | local seq_ids = {}
4 |
5 | local seq_id = 0;
6 |
7 | function get_network_seq_id(port_no)
8 | seq_ids[port_no] = (seq_ids[port_no] or 0) + 111
9 | seq_ids[port_no] = bit32.band(seq_ids[port_no], 0xFFFF)
10 | return seq_ids[port_no];
11 | end
12 |
13 | function set_network_seq_id(port_no, seq)
14 | seq_ids[port_no] = seq
15 | end
16 |
17 | -- local seq_id = 0;
18 |
19 | -- function get_network_seq_id()
20 | -- seq_id = seq_id + 111;
21 | -- seq_id = bit32.band(seq_id, 0xFFFF)
22 | -- return seq_id;
23 | -- end
24 |
25 | -- function set_network_seq_id(seq)
26 | -- seq_id = seq
27 | -- end
--------------------------------------------------------------------------------
/scripts/server/daemons/poker/poker_d.lua:
--------------------------------------------------------------------------------
1 | --roomd.lua
2 | --Created by wugd
3 | --负责扑克的相关模块
4 |
5 | --创建模块声明
6 | POKER_D = {}
7 | setmetatable(POKER_D, {__index = _G})
8 | local _ENV = POKER_D
9 |
10 | poker_data = {
11 | 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D, --方块 A - K
12 | 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D, --梅花 A - K
13 | 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D, --红桃 A - K
14 | 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D, --黑桃 A - K
15 | 0x4E,0x4F,
16 | }
17 |
18 | SET_TABLE_READ_ONLY(poker_data)
19 |
20 | function get_poker_data()
21 | return poker_data
22 | end
--------------------------------------------------------------------------------
/scripts/global/daemons/logd.lua:
--------------------------------------------------------------------------------
1 | -- logd.lua
2 | -- Created by wugd
3 | -- 日志记录
4 |
5 | -- 声明模块名
6 | LOG_D = {}
7 | setmetatable(LOG_D, {__index = _G})
8 | local _ENV = LOG_D
9 |
10 | local table_name = "log"
11 |
12 | function to_log(log_id, p1, p2, p3, memo, log_channel)
13 | local sql = SQL_D.insert_sql(table_name, {
14 | time = os.time(),
15 | log_id = log_id,
16 | p1 = p1,
17 | p2 = p2 or "",
18 | p3 = p3 or "",
19 | memo = memo or "",
20 | log_channel= log_channel or LOG_CHANNEL_NULL,
21 | })
22 | DB_D.execute_db(table_name, sql)
23 | end
24 |
25 | -- 模块的入口执行
26 | local function create()
27 | end
28 |
29 | create()
30 |
--------------------------------------------------------------------------------
/scripts/global/daemons/exitd.lua:
--------------------------------------------------------------------------------
1 | -- exitd.lua
2 | -- Created by wugd
3 | -- 结束进程
4 |
5 | -- 声明模块名
6 | EXIT_D = {}
7 | setmetatable(EXIT_D, {__index = _G})
8 | local _ENV = EXIT_D
9 |
10 | local shutdown_status = false
11 |
12 | -- 定义公共接口
13 |
14 | function exit()
15 | --TODO 保存数据库,关闭程序
16 |
17 | end
18 |
19 | --关闭服务器
20 | function shutdown()
21 | stop_server()
22 | -- CONNECT_D.closeConnectingInfo()
23 | --通知其它服务器关闭
24 | set_shutdown_status(true)
25 | exit()
26 | end
27 |
28 | function set_shutdown_status(flag)
29 | shutdown_status = flag
30 | end
31 |
32 | function is_shutdown()
33 | return shutdown_status
34 | end
35 |
36 | function create()
37 | end
38 | create()
39 |
--------------------------------------------------------------------------------
/scripts/client/daemons/stress_test/chat_testd.lua:
--------------------------------------------------------------------------------
1 | -- chat_testd.lua
2 | -- Created by wugd
3 | -- 聊天测试
4 |
5 | -- 声明模块名
6 | CHAT_TESTD = {}
7 | setmetatable(CHAT_TESTD, {__index = _G})
8 | local _ENV = CHAT_TESTD
9 |
10 | -- 达到每个玩家的该模块间隔时间,则调用该函数
11 | function operation(player)
12 | if not IS_OBJECT(player) then
13 | return
14 | end
15 | local content = player:query("name") .. "__" .. tostring(math.random(10000, 99999))
16 | player:send_message(CMD_CHAT, CHAT_CHANNEL_WORLD, {send_content = content});
17 | end
18 |
19 | --毫秒
20 | function random_interval()
21 | return math.random(10000, 300000)
22 | end
23 |
24 | -- 模块的入口函数
25 | function create()
26 |
27 | end
28 |
29 | create();
30 |
--------------------------------------------------------------------------------
/scripts/global/base/global_basic.lua:
--------------------------------------------------------------------------------
1 | -- global_basic.lua
2 | -- Created by wugd
3 | -- 维护基本对象
4 |
5 | --变量定义
6 | local class_basic_ob = {}
7 | local name_basic_ob = {}
8 |
9 | -- 定义公共接口,按照字母顺序排序
10 |
11 | --根据class_id查找对象
12 | function find_basic_object_by_class_id(class_id)
13 | return class_basic_ob[class_id]
14 | end
15 |
16 | --根据name查找对象
17 | function find_basic_object_by_name(name)
18 | return name_basic_ob[name]
19 | end
20 |
21 | --根据class_id设置对象
22 | function set_class_basic_object(class_id,basic_ob)
23 | class_basic_ob[class_id] = basic_ob
24 | end
25 |
26 | --根据name设置对象
27 | function set_name_basic_object(name,basic_ob)
28 | name_basic_ob[name] = basic_ob
29 | end
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/utils/lua_utils.rs:
--------------------------------------------------------------------------------
1 | use td_rlua::{self, lua_State};
2 | use libc;
3 | use std::mem;
4 | use std::ptr;
5 |
6 | pub struct LuaUtils {
7 |
8 | }
9 |
10 | impl LuaUtils {
11 | pub fn read_str_to_vec(lua: *mut lua_State, index: i32) -> Option> {
12 | let mut size: libc::size_t = unsafe { mem::MaybeUninit::uninit().assume_init() };
13 | let c_str_raw = unsafe { td_rlua::lua_tolstring(lua, index, &mut size) };
14 | if c_str_raw.is_null() {
15 | return None;
16 | }
17 | let mut dst = vec![0 as u8; size];
18 | unsafe {
19 | ptr::copy(c_str_raw as *mut u8, dst.as_mut_ptr(), size);
20 | }
21 | Some(dst)
22 | }
23 | }
--------------------------------------------------------------------------------
/scripts/server/redis_scripts/gate_select.lua:
--------------------------------------------------------------------------------
1 | local gate_matches = KEYS[1]
2 | local gate_prefix = ARGV[1]
3 |
4 | local reply = redis.call("KEYS", gate_matches)
5 | local ip, port, min = nil, nil, 9999999
6 | local match_string = string.format("%s:([%%w.]*):(%%d+)",gate_prefix)
7 | repeat
8 | if not reply then
9 | break
10 | end
11 | for _,value in ipairs(reply) do
12 | local cur_ip, cur_port = string.match(value, match_string)
13 | local reply_value = redis.call("get", value)
14 | if tonumber(cur_port) and (tonumber(reply_value) or 0) < min then
15 | ip, port, min = cur_ip, cur_port, tonumber(reply_value)
16 | end
17 | end
18 | until true
19 |
20 | return {ip, port}
21 |
--------------------------------------------------------------------------------
/scripts/global/base/heartbeat.lua:
--------------------------------------------------------------------------------
1 | -- heartbeat.lua
2 | -- Created by wugd
3 | -- 心跳相关函数
4 |
5 | -- 定义公共接口,按照字母顺序排序
6 |
7 | local heartbeat_funcs = {};
8 |
9 | -- 调用心跳回调函数
10 | function post_heartbeat(ob_class, ob)
11 | local func_list = heartbeat_funcs[ob_class];
12 | if not func_list then
13 | return;
14 | end
15 |
16 | -- 依次调用回调
17 | for _, f in ipairs(func_list) do
18 | if type(f) == "function" then
19 | f(ob);
20 | end
21 | end
22 | end
23 |
24 | -- 注册心跳回调函数
25 | function register_heartbeat(ob_class, f)
26 | if not IS_TABLE(heartbeat_funcs[ob_class]) then
27 | heartbeat_funcs[ob_class] = {};
28 | end
29 |
30 | table.insert(heartbeat_funcs[ob_class], f);
31 | end
32 |
--------------------------------------------------------------------------------
/src/utils/time_utils.rs:
--------------------------------------------------------------------------------
1 |
2 | use std::time::{SystemTime, UNIX_EPOCH};
3 |
4 |
5 | pub struct TimeUtils {
6 | }
7 |
8 | impl TimeUtils {
9 | pub fn get_time_s() -> u64 {
10 | let start = SystemTime::now();
11 | let since_the_epoch = start
12 | .duration_since(UNIX_EPOCH)
13 | .expect("Time went backwards");
14 | since_the_epoch.as_secs() as u64
15 | }
16 |
17 |
18 | pub fn get_time_ms() -> u64 {
19 | let start = SystemTime::now();
20 | let since_the_epoch = start
21 | .duration_since(UNIX_EPOCH)
22 | .expect("Time went backwards");
23 | let ms = since_the_epoch.as_secs() as u64 * 1000u64 + (since_the_epoch.subsec_nanos() as f64 / 1_000_000.0) as u64;
24 | ms
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/scripts/server/clone/rooms/ddz_room.lua:
--------------------------------------------------------------------------------
1 | --ddz_room.lua
2 | --Created by wugd
3 | --斗地主房间类
4 |
5 | --创建类模板
6 | DDZ_ROOM_TDCLS = tdcls(ROOM_TDCLS)
7 | DDZ_ROOM_TDCLS.name = "DDZ_ROOM_TDCLS"
8 |
9 | --构造函数
10 | function DDZ_ROOM_TDCLS:create(value)
11 | ASSERT(IS_TABLE(value), "room:create para not correct")
12 | end
13 |
14 | --获取房间类型
15 | function DDZ_ROOM_TDCLS:get_game_type()
16 | return "ddz"
17 | end
18 |
19 | function ROOM_TDCLS:get_ob_id()
20 | return (string.format("DDZ_ROOM_TDCLS:%s", SAVE_STRING(self:get_room_name())))
21 | end
22 |
23 | function DDZ_ROOM_TDCLS:entity_update(entity)
24 | local room_update = get_class_func(ROOM_TDCLS, "entity_update")
25 | room_update(self, entity)
26 | end
27 |
28 | function ROOM_TDCLS:get_desk_class()
29 | return DDZ_DESK_TDCLS
30 | end
--------------------------------------------------------------------------------
/src/lua_custom/mod.rs:
--------------------------------------------------------------------------------
1 | use td_rlua::Lua;
2 | use luacjson;
3 | use luasocket;
4 |
5 | mod lua_db;
6 | mod lua_network;
7 | mod lua_userdata;
8 | mod lua_timer;
9 | mod lua_util;
10 |
11 | pub use self::lua_db::register_db_func;
12 | pub use self::lua_network::register_network_func;
13 | pub use self::lua_userdata::register_userdata_func;
14 | pub use self::lua_timer::register_timer_func;
15 | pub use self::lua_util::register_util_func;
16 |
17 | pub fn register_custom_func(lua: &mut Lua) {
18 | register_db_func(lua);
19 | register_network_func(lua);
20 | register_userdata_func(lua);
21 | register_timer_func(lua);
22 | register_util_func(lua);
23 |
24 | luasocket::enable_socket_core(lua);
25 | luacjson::enable_cjson(lua);
26 | unsafe {
27 | luacjson::luaopen_cjson(lua.state());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/config/gate.yaml:
--------------------------------------------------------------------------------
1 | start_lua: server/server.lua
2 | telnet_addr: 127.0.0.1:9009
3 | lua_macros:
4 | SERVER_ID: 1
5 | _DEBUG: true
6 | SERVER_TYPE: gate
7 | CODE_TYPE: 1
8 | CODE_ID: 1
9 | ENABLE_ROOM: true
10 | GATE_IP: 127.0.0.1
11 | IS_SINGLE: true
12 | CHECK_PACK: true
13 | VERSION: 1
14 | GATE_WEBSOCKET_PORT: 11331
15 | GATE_HTTP_PORT: 11330
16 | GATE_CLIENT_PORT: 11329
17 | GATE_LOGIC_PORT: 11326
18 | CURRENT_IP: 127.0.0.1
19 | LOG_LEVEL: 5
20 | DB_SUFFIX: ""
21 | TABLE_SUFFIX: engine_
22 | DB_TYPE: mysql
23 | ENABLE_TEST: true
24 | ENABLE_DATABASE_SYNC: true
25 | LUA_HELPER_DEBUG_PORT: 8818
26 | SECRET_KEY: tunm_will_be_good
27 | db_info:
28 | mysql : mysql://root:erphuidfni!%40%2387899@rm-bp18407l4yw4y5130ho.mysql.rds.aliyuncs.com:3306
29 | redis : redis://i.haode.fit:6379/10
30 | sqlite: tunm.db
31 |
--------------------------------------------------------------------------------
/config/client.yaml:
--------------------------------------------------------------------------------
1 | start_lua: server/server.lua
2 | telnet_addr: 127.0.0.1:9009
3 | lua_macros:
4 | SERVER_ID: 1
5 | _DEBUG: true
6 | SERVER_TYPE: client
7 | CODE_TYPE: 1
8 | CODE_ID: 1
9 | ENABLE_ROOM: true
10 | GATE_IP: 127.0.0.1
11 | IS_SINGLE: true
12 | CHECK_PACK: true
13 | VERSION: 1
14 | GATE_WEBSOCKET_PORT: 11331
15 | GATE_HTTP_PORT: 11330
16 | GATE_CLIENT_PORT: 11329
17 | GATE_LOGIC_PORT: 11326
18 | CURRENT_IP: 127.0.0.1
19 | LOG_LEVEL: 5
20 | DB_SUFFIX: ""
21 | TABLE_SUFFIX: engine_
22 | DB_TYPE: mysql
23 | ENABLE_TEST: true
24 | ENABLE_DATABASE_SYNC: true
25 | LUA_HELPER_DEBUG_PORT: 8818
26 | SECRET_KEY: tunm_will_be_good
27 | db_info:
28 | mysql : mysql://root:erphuidfni!%40%2387899@rm-bp18407l4yw4y5130ho.mysql.rds.aliyuncs.com:3306
29 | redis : redis://i.haode.fit:6379/10
30 | sqlite: tunm.db
31 |
--------------------------------------------------------------------------------
/config/server.yaml:
--------------------------------------------------------------------------------
1 | start_lua: server/server.lua
2 | telnet_addr: 127.0.0.1:9009
3 | lua_macros:
4 | SERVER_ID: 1
5 | _DEBUG: true
6 | SERVER_TYPE: game
7 | CODE_TYPE: 3
8 | CODE_ID: 1
9 | ENABLE_ROOM: true
10 | GATE_IP: 127.0.0.1
11 | IS_SINGLE: true
12 | CHECK_PACK: true
13 | VERSION: 1
14 | GATE_WEBSOCKET_PORT: 11331
15 | GATE_HTTP_PORT: 11330
16 | GATE_CLIENT_PORT: 11329
17 | GATE_LOGIC_PORT: 11326
18 | CURRENT_IP: 127.0.0.1
19 | LOG_LEVEL: 5
20 | DB_SUFFIX: ""
21 | TABLE_SUFFIX: engine_
22 | DB_TYPE: mysql
23 | ENABLE_TEST: true
24 | ENABLE_DATABASE_SYNC: true
25 | LUA_HELPER_DEBUG_PORT: 8818
26 | SECRET_KEY: tunm_will_be_good
27 | db_info:
28 | mysql : mysql://root:erphuidfni!%40%2387899@rm-bp18407l4yw4y5130ho.mysql.rds.aliyuncs.com:3306
29 | redis : redis://i.haode.fit:6379/10
30 | sqlite: tunm.db
31 |
--------------------------------------------------------------------------------
/scripts/global/base/global_data_boot.lua:
--------------------------------------------------------------------------------
1 | -- global_data_boot.lua
2 | -- Created by wugd
3 | -- 全局启动数据加载相关模块,数据加载完毕才启动
4 |
5 | --变量定义
6 | local post_data_init_list = {};
7 | local need_load_data_num = 0
8 | -- 定义公共接口,按照字母顺序排序
9 |
10 | -- 依次调用初始化函数
11 | local function post_data_init()
12 | local temp_post_init = DUP(post_data_init_list);
13 |
14 | -- 先清空,避免递归调用
15 | post_data_init_list = {};
16 | for _, f in ipairs(temp_post_init) do
17 | f();
18 | end
19 | end
20 |
21 | function register_post_data_init(f)
22 | post_data_init_list[#post_data_init_list + 1] = f;
23 | end
24 |
25 | function set_need_load_data_num(num)
26 | need_load_data_num = num
27 | end
28 |
29 | function finish_one_load_data()
30 | need_load_data_num = need_load_data_num - 1
31 | if need_load_data_num <= 0 then
32 | post_data_init()
33 | end
34 | end
--------------------------------------------------------------------------------
/scripts/global/inherit/rid.lua:
--------------------------------------------------------------------------------
1 | -- rid.lua
2 | -- Created by wugd
3 | -- RID基类
4 |
5 | -- 创建类模板
6 | RID_TDCLS = tdcls();
7 | RID_TDCLS.name = "RID_TDCLS";
8 |
9 | -- 构造函数
10 | function RID_TDCLS:create(para)
11 | if not para then
12 | return;
13 | end
14 | local rid = para["rid"];
15 | if rid then
16 | self:set_rid(rid);
17 | end
18 | end
19 |
20 | -- 析构函数
21 | function RID_TDCLS:destruct()
22 | local rid = self:query("rid");
23 | if rid then
24 | remove_rid_object(rid, self);
25 | end
26 | end
27 |
28 | -- 定义公共接口,按照字母顺序排序
29 |
30 | -- 取得 rid
31 | function RID_TDCLS:GET_RID()
32 | return (self:query("rid"));
33 | end
34 |
35 | -- 设置对象的RID,每个对象的RID只能被设置一次
36 | function RID_TDCLS:set_rid(rid)
37 | ASSERT(not self:query("rid"), "");
38 |
39 | self:set("rid", rid);
40 | set_rid_object(rid, self);
41 | end
42 |
--------------------------------------------------------------------------------
/scripts/server/daemons/containerd.lua:
--------------------------------------------------------------------------------
1 | -- containerd.lua
2 | -- Created by wugd
3 | -- 容器相关功能
4 |
5 | -- 声明模块名
6 | CONTAINER_D = {}
7 | setmetatable(CONTAINER_D, {__index = _G})
8 | local _ENV = CONTAINER_D
9 |
10 | -- 定义公共接口,按照字母顺序排序
11 |
12 | -- 取得物件对应的格子页面
13 | function get_page(ob)
14 | local ob_type
15 | if IS_OBJECT(ob) then
16 | ob_type = ob:query("ob_type")
17 | if ob_type == OB_TYPE_ITEM or ob_type == OB_TYPE_EQUIP then
18 | -- 若为道具,则 ob_type 即为页面号
19 | return (ob:query("ob_type"))
20 | end
21 | end
22 | end
23 |
24 | function get_page_by_data(info)
25 | local ob_type = info["ob_type"]
26 | if ob_type == OB_TYPE_ITEM or ob_type == OB_TYPE_EQUIP then
27 | -- 若为道具,则 ob_type 即为页面号
28 | return info["ob_type"]
29 | end
30 | end
31 |
32 |
33 | -- 模块的入口执行
34 | function create()
35 | end
36 |
37 | create()
38 |
--------------------------------------------------------------------------------
/scripts/global/base/rid.lua:
--------------------------------------------------------------------------------
1 | -- rid.lua
2 | -- Created by wugd
3 | -- 维护运行中ID
4 |
5 | -- 全局变量声明
6 |
7 | -- 私有变量声明
8 | local rid_objects = {};
9 |
10 | -- 根据RID取对象
11 | function find_object_by_rid(rid)
12 | return rid_objects[rid];
13 | end
14 |
15 | -- 取映射表
16 | function query_rid_objects()
17 | return rid_objects;
18 | end
19 |
20 | -- 取消RID和对象的映射关系
21 | -- 只有拥有RID对象本身允许进行这个调用
22 | function remove_rid_object(rid, caller)
23 | ASSERT(rid_objects[rid] == caller, "");
24 | rid_objects[rid] = nil;
25 | end
26 |
27 | -- 增加RID和对象的映射关系
28 | function set_rid_object(rid, ob)
29 | ASSERT(rid_objects[rid] == nil, "");
30 | rid_objects[rid] = ob;
31 | end
32 |
33 | -- 生成新的RID
34 | function NEW_RID(flag)
35 | flag = flag or 1;
36 | return (GET_NEXT_RID(tonumber(SERVER_ID), flag));
37 | end
38 |
39 | -- 快捷访问宏:仅供控制台调试时使用
40 | function RID(rid)
41 | return (find_object_by_rid(rid));
42 | end
43 |
--------------------------------------------------------------------------------
/scripts/global/inherit/property.lua:
--------------------------------------------------------------------------------
1 | -- property.lua
2 | -- Created by wugd
3 | -- 物件基类
4 |
5 | -- 创建类模板
6 | PROPERTY_TDCLS = tdcls();
7 | PROPERTY_TDCLS.name = "PROPERTY_TDCLS";
8 |
9 | -- 构造函数
10 | function PROPERTY_TDCLS:create(para)
11 | end
12 |
13 | -- 析构函数
14 | function PROPERTY_TDCLS:destruct()
15 |
16 | end
17 |
18 | -- 定义公共接口,按照字母顺序排序
19 | -- 取得该物件所在的容器对象
20 | function PROPERTY_TDCLS:get_container()
21 | ASSERT(self:query("owner") == self:query_temp("container"));
22 |
23 | local owner_rid = self:query("owner");
24 | if owner_rid then
25 | return (find_object_by_rid(owner_rid));
26 | end
27 | end
28 |
29 | -- 取得该物件所在的属主对象
30 | function PROPERTY_TDCLS:get_owner()
31 | ASSERT(self:query("owner") == self:query_temp("container"));
32 |
33 | local owner_rid = self:query("owner");
34 | if owner_rid then
35 | return (find_object_by_rid(owner_rid));
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/scripts/global/base/global_boot.lua:
--------------------------------------------------------------------------------
1 | -- global_boot.lua
2 | -- Created by wugd
3 | -- 全局启动相关工具函数
4 |
5 | --变量定义
6 | local post_init_list = {};
7 | local server_list_callback_list = {};
8 | setmetatable(server_list_callback_list, { __mode = "v" });
9 |
10 | -- 定义公共接口,按照字母顺序排序
11 |
12 | -- 依次调用初始化函数
13 | function post_init()
14 | local temp_post_init = DUP(post_init_list);
15 |
16 | -- 先清空,避免递归调用
17 | post_init_list = {};
18 | for _, f in ipairs(temp_post_init) do
19 | f();
20 | end
21 | end
22 |
23 | function register_post_init(f)
24 | post_init_list[#post_init_list + 1] = f;
25 | end
26 |
27 | function register_server_list_done(f)
28 | server_list_callback_list[#server_list_callback_list + 1] = f;
29 | end
30 |
31 | function post_server_list_done()
32 | CLEAN_ARRAY(server_list_callback_list);
33 | for _, f in ipairs(server_list_callback_list) do
34 | if type(f) == "function" then
35 | xpcall(f, ERROR_HANDLE);
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/src/mgr/command_mgr.rs:
--------------------------------------------------------------------------------
1 | use {ThreadUtils, LuaEngine};
2 | pub struct CommandMgr;
3 |
4 | static COMMAND_POOL_NAME: &'static str = "command";
5 |
6 | impl CommandMgr {
7 | pub fn start_command_input() {
8 | let pool = ThreadUtils::instance().get_pool(&COMMAND_POOL_NAME.to_string());
9 | pool.execute(move || {
10 | loop {
11 | let mut line = String::new();
12 | let _ = unwrap_or!(::std::io::stdin().read_line(&mut line).ok(), 0);
13 | if line.is_empty() {
14 | ::std::thread::sleep(::std::time::Duration::from_millis(100));
15 | continue;
16 | }
17 | let line = line.trim_matches(|c| c == '\r' || c == '\n').to_string();
18 | if line == "quit" {
19 | break;
20 | }
21 | let line = LuaEngine::convert_excute_string(line);
22 | LuaEngine::instance().apply_exec_string(line);
23 | }
24 | });
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/scripts/global/inherit/attrib.lua:
--------------------------------------------------------------------------------
1 | -- attrib.lua
2 | -- Created by wugd
3 | -- 属性基类
4 |
5 | -- 创建类模板
6 | ATTRIB_TDCLS = tdcls();
7 | ATTRIB_TDCLS.name = "ATTRIB_TDCLS";
8 |
9 | -- 构造函数
10 | function ATTRIB_TDCLS:create(para)
11 | end
12 |
13 | -- 定义公共接口,按照字母顺序排序
14 |
15 | -- 查询对象属性值
16 | function ATTRIB_TDCLS:query_attrib(key, raw)
17 | return (ATTRIB_D.query_attrib(self, key));
18 | end
19 |
20 | -- 增加属性操作
21 | function ATTRIB_TDCLS:add_attrib(field, value)
22 | return (ATTRIB_D.add_attrib(self, field, value));
23 | end
24 |
25 | -- 消耗属性操作
26 | function ATTRIB_TDCLS:cost_attrib(field, value)
27 | return (ATTRIB_D.cost_attrib(self, field, value));
28 | end
29 |
30 | -- 查询属性是性足够
31 | function ATTRIB_TDCLS:has_attrib(info)
32 | for k,v in pairs(info) do
33 | if self:query_attrib(k) < v then
34 | return false, k
35 | end
36 | end
37 | return true
38 | end
39 |
40 | -- 扣除批量属性
41 | function ATTRIB_TDCLS:cost_attribs(info)
42 | for k,v in pairs(info) do
43 | self:cost_attrib(k, v)
44 | end
45 | end
--------------------------------------------------------------------------------
/scripts/test/fix.lua:
--------------------------------------------------------------------------------
1 | local tests = {};
2 |
3 | local function test()
4 | TRACE("test")
5 | end
6 | -- 取得所有的 agent
7 | function get_tests()
8 | return tests;
9 | end
10 |
11 | function insert_test(v)
12 | table.insert(tests, v)
13 | test()
14 | end
15 |
16 | function sub_test(v)
17 | local tests = "aa"
18 | local function test()
19 | TRACE("tests = %o", tests)
20 | end
21 | test()
22 | end
23 |
24 | -- function set_port_map(first, second)
25 | -- tests[first] = true
26 | -- tests[second] = true
27 | -- end
28 |
29 | -- function get_port_map()
30 | -- return tests
31 | -- end
32 |
33 | local port_map = {};
34 | function get_port_map()
35 | TRACE("1111111111111111")
36 | return port_map
37 | end
38 |
39 | function set_port_map(port_no_server, port_no_client)
40 | TRACE("22222222222222222")
41 | port_map[port_no_server] = port_map[port_no_server] or {}
42 | port_map[port_no_client] = port_map[port_no_client] or {}
43 | port_map[port_no_server][port_no_client] = true
44 | port_map[port_no_client][port_no_server] = true
45 | end
46 |
--------------------------------------------------------------------------------
/src/db/db_trait.rs:
--------------------------------------------------------------------------------
1 | use NetResult;
2 | use NetMsg;
3 |
4 | /// DbTrait is the interface for db, if you would support new db, impl it
5 | pub trait DbTrait
6 | {
7 | /// data will store in NetMsg, if return i32 not 0, it may occur sql error
8 | fn select(&mut self, sql_cmd: &str, msg: &mut NetMsg) -> NetResult;
9 | /// execute sql, but not store data, if return i32 not 0, it may occur sql error
10 | fn execute(&mut self, sql_cmd: &str) -> NetResult;
11 | /// data will store in NetMsg, if return i32 not 0, it may occur sql error, you can call get_last_insert_id get insert id
12 | fn insert(&mut self, sql_cmd: &str, msg: &mut NetMsg) -> NetResult;
13 | fn begin_transaction(&mut self) -> NetResult;
14 | fn commit_transaction(&mut self) -> NetResult;
15 | fn rollback_transaction(&mut self) -> NetResult;
16 | fn get_last_insert_id(&mut self) -> u64;
17 | fn get_affected_rows(&mut self) -> u64;
18 | fn get_character_set(&mut self) -> u8;
19 | fn is_connected(&mut self) -> bool;
20 | fn get_error_code(&mut self) -> i32;
21 | fn get_error_str(&mut self) -> Option;
22 | }
23 |
--------------------------------------------------------------------------------
/scripts/data/txt/exp_user.txt:
--------------------------------------------------------------------------------
1 | #经验配置表
2 | #等级 到达下级所需的经验
3 | int int
4 | level exp
5 | 1 10
6 | 2 10
7 | 3 10
8 | 4 15
9 | 5 15
10 | 6 15
11 | 7 25
12 | 8 35
13 | 9 45
14 | 10 55
15 | 11 65
16 | 12 75
17 | 13 85
18 | 14 95
19 | 15 105
20 | 16 115
21 | 17 125
22 | 18 135
23 | 19 145
24 | 20 155
25 | 21 175
26 | 22 195
27 | 23 215
28 | 24 235
29 | 25 255
30 | 26 315
31 | 27 375
32 | 28 435
33 | 29 495
34 | 30 555
35 | 31 675
36 | 32 795
37 | 33 915
38 | 34 1035
39 | 35 1155
40 | 36 1315
41 | 37 1475
42 | 38 1635
43 | 39 1795
44 | 40 1955
45 | 41 2195
46 | 42 2435
47 | 43 2675
48 | 44 2915
49 | 45 3155
50 | 46 3395
51 | 47 3635
52 | 48 3875
53 | 49 4115
54 | 50 4355
55 | 51 4595
56 | 52 4835
57 | 53 5075
58 | 54 5315
59 | 55 5555
60 | 56 5795
61 | 57 6035
62 | 58 6275
63 | 59 6515
64 | 60 6755
65 | 61 6995
66 | 62 7235
67 | 63 7475
68 | 64 7715
69 | 65 7955
70 | 66 8195
71 | 67 8435
72 | 68 8675
73 | 69 8915
74 | 70 9155
75 | 71 9395
76 | 72 9635
77 | 73 9875
78 | 74 10115
79 | 75 10355
80 | 76 10595
81 | 77 10835
82 | 78 11075
83 | 79 11315
84 | 80 11555
85 | 81 11795
86 | 82 12035
87 | 83 12275
88 | 84 12515
89 | 85 12755
90 | 86 12995
91 | 87 13235
92 | 88 13475
93 | 89 13715
94 | 90 13955
95 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | Copyright (c) 2006-2009 Graydon Hoare
2 | Copyright (c) 2009-2013 Mozilla Foundation
3 |
4 | Permission is hereby granted, free of charge, to any
5 | person obtaining a copy of this software and associated
6 | documentation files (the "Software"), to deal in the
7 | Software without restriction, including without
8 | limitation the rights to use, copy, modify, merge,
9 | publish, distribute, sublicense, and/or sell copies of
10 | the Software, and to permit persons to whom the Software
11 | is furnished to do so, subject to the following
12 | conditions:
13 |
14 | The above copyright notice and this permission notice
15 | shall be included in all copies or substantial portions
16 | of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
19 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
20 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
21 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
22 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
25 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 | DEALINGS IN THE SOFTWARE.
27 |
--------------------------------------------------------------------------------
/scripts/global/daemons/forbidden_wordd.lua:
--------------------------------------------------------------------------------
1 | --forbidden_wordd.lua
2 | --屏蔽字库
3 | --create by wugd
4 | FORBIDDEN_WORDD = {}
5 | setmetatable(FORBIDDEN_WORDD, {__index = _G})
6 | local _ENV = FORBIDDEN_WORDD
7 |
8 | local forbidden_table = {}
9 | local max_length = 1
10 |
11 | function get_forbidden_list()
12 | return forbidden_table
13 | end
14 |
15 | function load_forbidden_word(file)
16 | local fp = io.open(GET_FULL_PATH(file))
17 | forbidden_table = {}
18 | if fp then
19 | for line in fp:lines() do
20 | forbidden_table[line] = true
21 | max_length = math.max(max_length, string.len(line))
22 | end
23 | io.close(fp)
24 | end
25 | end
26 |
27 | function has_forbidden_word(content)
28 | local wordLen = string.len(content)
29 | for len=0, max_length do
30 | for i= 1, wordLen - len, 1 do
31 | local word = string.sub(content, i, i + len)
32 | if forbidden_table[word] then
33 | return word
34 | end
35 | end
36 | end
37 | return nil
38 | end
39 |
40 |
41 | function create()
42 | load_forbidden_word("data/txt/banned_word.txt")
43 | end
44 |
45 | create()
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/net/mod.rs:
--------------------------------------------------------------------------------
1 | mod net_msg;
2 | mod socket_event;
3 |
4 | pub use self::net_msg::NetMsg;
5 | pub use self::net_msg::MSG_TYPE_TD;
6 | pub use self::net_msg::MSG_TYPE_JSON;
7 | pub use self::net_msg::MSG_TYPE_BIN;
8 | pub use self::net_msg::MSG_TYPE_TEXT;
9 | pub use self::socket_event::{SocketEvent, ReadCb, AcceptCb, WriteCb, EndCb};
10 |
11 |
12 | #[cfg(unix)]
13 | use std::os::unix::io::{AsRawFd};
14 | #[cfg(target_os = "wasi")]
15 | use std::os::wasi::io::{AsRawFd};
16 | #[cfg(windows)]
17 | use std::os::windows::io::{AsRawSocket};
18 |
19 | pub trait AsSocket {
20 | fn as_socket(&self) -> usize;
21 |
22 | }
23 | use mio::net::{TcpListener, TcpStream};
24 |
25 | impl AsSocket for TcpStream {
26 | #[cfg(unix)]
27 | fn as_socket(&self) -> usize {
28 | return self.as_raw_fd() as usize;
29 | }
30 |
31 | #[cfg(windows)]
32 | fn as_socket(&self) -> usize {
33 | return self.as_raw_socket() as usize;
34 | }
35 | }
36 |
37 |
38 | impl AsSocket for TcpListener {
39 | #[cfg(unix)]
40 | fn as_socket(&self) -> usize {
41 | return self.as_raw_fd() as usize;
42 | }
43 |
44 | #[cfg(windows)]
45 | fn as_socket(&self) -> usize {
46 | return self.as_raw_socket() as usize;
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/scripts/global/base/string_util.lua:
--------------------------------------------------------------------------------
1 | -- string_util.lua
2 | -- 字符帮助类
3 |
4 | local empty_filter = " "
5 | local empty_map = {}
6 |
7 | function is_contain_empty_char(str)
8 | local map = get_str_map(str)
9 | for empty,_ in pairs(empty_map) do
10 | if map[empty] then
11 | return true
12 | end
13 | end
14 | return false
15 | end
16 |
17 | function get_utf8_count(str)
18 | local _, count = string.gsub(str, "[^\128-\193]", "")
19 | return count
20 | end
21 |
22 | function get_utf8_logic_len(str)
23 | local sum = 0
24 | local tab = get_str_table(str)
25 | for _,value in ipairs(tab) do
26 | if string.len(value) > 1 then
27 | sum = sum + 2
28 | else
29 | sum = sum + 1
30 | end
31 | end
32 | return sum
33 | end
34 |
35 | function get_str_table(str)
36 | local tab = {}
37 | for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do
38 | tab[#tab+1] = uchar
39 | end
40 | return tab
41 | end
42 |
43 | function get_str_map(str)
44 | local tab = {}
45 | for uchar in string.gmatch(str, "[%z\1-\127\194-\244][\128-\191]*") do
46 | tab[uchar] = true
47 | end
48 | return tab
49 | end
50 |
51 |
52 | local function create()
53 | empty_map = get_str_map(empty_filter)
54 | end
55 |
56 | create()
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "tunm"
3 | version = "0.2.1"
4 | authors = [ "tickbh " ]
5 |
6 | description = "game server for Rust"
7 | repository = "https://github.com/tickbh/tunm"
8 | readme = "README.md"
9 | license = "MIT/Apache-2.0"
10 | keywords = ["gameserver", "server", "lua", "rust_lua"]
11 |
12 | [features]
13 |
14 | [dependencies]
15 | libc = "^0.2"
16 | time = "0.3.9"
17 | chrono = "0.4.19"
18 | net2 = { version = "0.2.23", default-features = false }
19 | rust-crypto = "0.2.34"
20 | tiny_http = { version = "0.11.0", default-features = false }
21 | td_rlua = "0.3.1"
22 | td_proto_rust = "0.1.2"
23 | tunm_proto = "0.1.12"
24 | td_rredis = "0.1.0"
25 | td_rthreadpool = "0.1.1"
26 | sys-info = "0.9.1"
27 | url = "2.2.2"
28 | psocket="0.1.5"
29 | log="0.4.17"
30 | env_logger = "0.9.0"
31 | log4rs = "1.1.1"
32 | reqwest = { version = "0.11", features = ["blocking", "json"] }
33 | mysql = { version = "23.0.1" }
34 | commander = "0.1"
35 | mio = "0.8.3"
36 | # websocket-simple="0.1.1"
37 | serde="1.0.137"
38 | luacjson="0.1.3"
39 | luasocket="0.1.10"
40 |
41 | tunm_timer="0.1.4"
42 | serde_json="1.0.81"
43 | serde_yaml="0.8.24"
44 |
45 | rand = "0.8.4"
46 |
47 | ws = "0.9.2"
48 | # ws = { branch="new", git = "https://github.com/tickbh/ws-rs.git"}
49 |
50 | [dependencies.rusqlite]
51 | version = "0.27.0"
52 | features = ["bundled"]
53 |
54 |
--------------------------------------------------------------------------------
/src/mgr/tcp_mgr.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use std::net::{TcpListener, TcpStream};
3 |
4 | pub struct TcpMgr {
5 | stream_fds: HashMap,
6 | listen_fds: HashMap,
7 | }
8 |
9 | static mut EL: *mut TcpMgr = 0 as *mut _;
10 | impl TcpMgr {
11 | pub fn instance() -> &'static mut TcpMgr {
12 | unsafe {
13 | if EL == 0 as *mut _ {
14 | EL = Box::into_raw(Box::new(TcpMgr::new()));
15 | }
16 | &mut *EL
17 | }
18 | }
19 |
20 | pub fn new() -> TcpMgr {
21 | TcpMgr {
22 | stream_fds: HashMap::new(),
23 | listen_fds: HashMap::new(),
24 | }
25 | }
26 |
27 | pub fn insert_stream(&mut self, fd: i32, stream: TcpStream) {
28 | self.stream_fds.insert(fd, stream);
29 | }
30 |
31 | pub fn remove_stream(&mut self, fd: i32) {
32 | self.stream_fds.remove(&fd);
33 | }
34 |
35 | pub fn get_stream(&mut self, fd: i32) -> Option<&mut TcpStream> {
36 | self.stream_fds.get_mut(&fd)
37 | }
38 |
39 | pub fn insert_listen(&mut self, fd: i32, listen: TcpListener) {
40 | self.listen_fds.insert(fd, listen);
41 | }
42 |
43 | pub fn remove_listen(&mut self, fd: i32) {
44 | self.listen_fds.remove(&fd);
45 | }
46 |
47 | pub fn get_listen(&mut self, fd: i32) -> Option<&mut TcpListener> {
48 | self.listen_fds.get_mut(&fd)
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/scripts/global/inherit/heartbeat.lua:
--------------------------------------------------------------------------------
1 | -- heartbeat.lua
2 | -- Created by wugd
3 | -- 心跳基类
4 |
5 | -- 创建类模板
6 | HEARTBEAT_TDCLS = tdcls();
7 | HEARTBEAT_TDCLS.name = "HEARTBEAT_TDCLS";
8 |
9 | -- 构造函数
10 | function HEARTBEAT_TDCLS:create(para)
11 | self.heartbeat_timer = -1;
12 | self.interval = 0.0;
13 | self.is_destructed = false;
14 | end
15 |
16 | -- 析构函数
17 | function HEARTBEAT_TDCLS:destruct()
18 | if IS_VALID_TIMER(self.heartbeat_timer) then
19 | delete_timer(self.heartbeat_timer);
20 | self.heartbeat_timer = -1;
21 | end
22 | self.is_destructed = true;
23 | end
24 |
25 | -- 心跳函数
26 | function HEARTBEAT_TDCLS:do_heartbeat()
27 | if self.is_destructed then
28 | delete_timer(self.heartbeat_timer);
29 | self.heartbeat_timer = -1;
30 | end
31 |
32 | -- 执行心跳回调函数
33 | xpcall(post_heartbeat, ERROR_HANDLE, self:get_ob_class(), self);
34 | end
35 |
36 | -- 定义公共接口,按照字母顺序排序
37 |
38 | -- 设置心跳时间
39 | function HEARTBEAT_TDCLS:delete_hearbeat()
40 | if IS_VALID_TIMER(self.heartbeat_timer) then
41 | delete_timer(self.heartbeat_timer);
42 | self.heartbeat_timer = -1;
43 | end
44 | end
45 |
46 | -- 设置心跳时间
47 | function HEARTBEAT_TDCLS:set_heartbeat_interval(_interval)
48 | self:delete_hearbeat()
49 | ASSERT(_interval >= 10000);
50 |
51 | -- 开始心跳
52 | self.interval = _interval;
53 | self.heartbeat_timer = set_timer(_interval, self.do_heartbeat, self, true);
54 | end
55 |
--------------------------------------------------------------------------------
/scripts/global/clone/queue.lua:
--------------------------------------------------------------------------------
1 | -- equip.lua
2 | -- Created by wugd
3 | -- 装备对象类
4 |
5 | -- 创建类模板
6 | QUEUE_TDCLS = tdcls()
7 | QUEUE_TDCLS.name = "QUEUE_TDCLS"
8 |
9 | -- 构造函数
10 | function QUEUE_TDCLS:create()
11 | self.first = 0
12 | self.last = -1
13 | self.data = {}
14 | end
15 |
16 |
17 | function QUEUE_TDCLS:push_front(value)
18 | local first = self.first - 1
19 | self.first = first
20 | self.data[first] = value
21 | return 1
22 | end
23 |
24 | function QUEUE_TDCLS:push_pack(queue, value)
25 | self.last = self.last + 1
26 | self.data[last] = value
27 | return self.last - self.first + 1
28 | end
29 |
30 | function QUEUE_TDCLS:pop_first()
31 | if self.first > self.last then
32 | return
33 | end
34 | local value = self.data[self.first]
35 | self.data[self.first] = nil
36 | self.first = first + 1
37 | return value
38 | end
39 |
40 | function QUEUE_TDCLS:pop_last()
41 | if self.first > self.last then
42 | return
43 | end
44 | local value = self.data[self.last]
45 | self.data[self.last] = nil
46 | self.last = self.last - 1
47 | return value
48 | end
49 |
50 | function QUEUE_TDCLS:get_size()
51 | if self.first > self.last then
52 | return 0
53 | end
54 | return self.last - self.first + 1
55 | end
56 |
57 | function QUEUE_TDCLS:get_first(queue)
58 | return self.data[self.first]
59 | end
60 |
61 | function QUEUE_TDCLS:get_data()
62 | return self.data
63 | end
--------------------------------------------------------------------------------
/scripts/global/daemons/systemd.lua:
--------------------------------------------------------------------------------
1 | -- systemd.lua
2 | -- Created by wud
3 | -- 负责机器当前情况
4 |
5 | -- 声明模块名
6 | SYSTEM_D = {}
7 | setmetatable(SYSTEM_D, {__index = _G})
8 | local _ENV = SYSTEM_D
9 |
10 | local cpu_num = 0
11 | -- Such as 2500, that is 2500 MHz.
12 | local cpu_speed = 0
13 | local os_type = ""
14 | local os_release = ""
15 | --{ "one", "five", "fifteen" }
16 | local load_avg = {}
17 | local proc_total = 0
18 | --{ "total", "free", "avail", "buffers", "cached", "swap_total", "swap_free" }
19 | local mem_info = {}
20 | --{ "total", "free" }
21 | local disk_info = {}
22 |
23 | function get_cpu_ratio_avg()
24 | return (load_avg["one"] or 0.1) * 100
25 | end
26 |
27 | function get_memory_use_ratio()
28 | mem_info["total"] = mem_info["total"] or 1
29 | mem_info["free"] = mem_info["free"] or 1
30 | if mem_info["total"] == 0 then
31 | TRACE("memory get error")
32 | return 0
33 | end
34 | return mem_info["free"] / mem_info["total"] * 100
35 | end
36 |
37 | function reload_mem_loadavg()
38 | load_avg = SYSTEM_LOADAVG()
39 | proc_total = SYSTEM_PROC_TOTAL()
40 | mem_info = SYSTEM_MEM_INFO()
41 | end
42 |
43 | local function create()
44 | cpu_num = SYSTEM_CPU_NUM()
45 | cpu_speed = SYSTEM_CPU_SPEED()
46 | os_type = SYSTEM_OS_TYPE()
47 | os_release = SYSTEM_OS_RELEASE()
48 | disk_info = SYSTEM_DISK_INFO()
49 |
50 | reload_mem_loadavg()
51 | set_timer(1000 * 60, reload_mem_loadavg, nil, true)
52 | end
53 |
54 | create()
--------------------------------------------------------------------------------
/src/utils/thread_utils.rs:
--------------------------------------------------------------------------------
1 | use ThreadPool;
2 | use std::collections::HashMap;
3 | /// the thread pool info
4 | pub struct ThreadUtils {
5 | pools: HashMap,
6 | }
7 |
8 | static mut EL: *mut ThreadUtils = 0 as *mut _;
9 | const DEFAULT_THREADS: usize = 1;
10 |
11 | impl ThreadUtils {
12 | pub fn instance() -> &'static mut ThreadUtils {
13 | unsafe {
14 | if EL == 0 as *mut _ {
15 | let config = ThreadUtils { pools: HashMap::new() };
16 | EL = Box::into_raw(Box::new(config));
17 | }
18 | &mut *EL
19 | }
20 | }
21 |
22 | pub fn create_pool(&mut self, name: String, threads: usize) {
23 | let pool = ThreadPool::new_with_name(threads, name.clone());
24 | self.pools.insert(name, pool);
25 | }
26 |
27 | pub fn get_pool(&mut self, name: &String) -> &mut ThreadPool {
28 | if !self.pools.contains_key(name) {
29 | self.pools.insert(name.clone(),
30 | ThreadPool::new_with_name(DEFAULT_THREADS, name.clone()));
31 | }
32 | self.pools.get_mut(name).unwrap()
33 | }
34 |
35 | pub fn get_default_pool(&mut self, name: &String, threads: usize) -> &mut ThreadPool {
36 | if !self.pools.contains_key(name) {
37 | self.pools.insert(name.clone(),
38 | ThreadPool::new_with_name(threads, name.clone()));
39 | }
40 | self.pools.get_mut(name).unwrap()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/scripts/server/daemons/package_statd.lua:
--------------------------------------------------------------------------------
1 |
2 | PACKAGE_STATD = {}
3 | setmetatable(PACKAGE_STATD, {__index = _G})
4 | local _ENV = PACKAGE_STATD
5 | -- 玩家接收到的包裹统计
6 |
7 | MAX_PACKAGE_ONESEC = 30 -- 每秒最多允许30个包
8 | MAX_PACKAGE_3SEC = 20 -- 3秒连续都超过这个包数,则踢掉
9 |
10 | -- 初始化统计表
11 | function init_stat_data()
12 | local t =
13 | {
14 | last_second = os.time() , -- 当前统计秒内
15 | recv_count = 0 , -- 寻前统计到的包累加
16 | last_second_recv_total = 0 , -- 保存前一秒统计值
17 | three_second_counter = 0 , -- 连续超过持续包统计的秒数
18 | }
19 | return t
20 | end
21 |
22 | function on_user_recv_package( user )
23 | local stat = user:query_temp("package_stat")
24 | if not stat then
25 | stat = init_stat_data()
26 | user:set_temp("package_stat", stat)
27 | end
28 | local ret = on_stat_recv_package(stat)
29 | if ret ~= 0 then
30 | user:connection_lost(true)
31 | end
32 | end
33 |
34 | function on_stat_recv_package( t )
35 | t.recv_count = t.recv_count + 1 -- 包数+1
36 | local second = os.time()
37 | if second ~= t.last_second then
38 | t.last_second = second
39 | t.last_second_recv_total = t.recv_count
40 | t.recv_count = 0
41 |
42 | if t.last_second_recv_total>= MAX_PACKAGE_3SEC then
43 | t.three_second_counter = t.three_second_counter + 1
44 | else
45 | t.three_second_counter = 0
46 | end
47 |
48 | if t.last_second_recv_total>MAX_PACKAGE_ONESEC then
49 | return 1 -- 超过每秒最大包数
50 | elseif t.three_second_counter>3 then
51 | return 2 -- 超过持续包数
52 | end
53 | end
54 | return 0
55 | end
56 |
57 |
--------------------------------------------------------------------------------
/scripts/client/client.lua:
--------------------------------------------------------------------------------
1 |
2 |
3 | -- 更新一个文件,强制重新载入
4 | function update(name)
5 | name = string.gsub(name, ".lua", "") .. ".lua";
6 | local full_name = GET_FULL_PATH(name);
7 | require(full_name);
8 | TRACE("update file name = %o", name)
9 | -- 回收垃圾
10 | collectgarbage("collect");
11 | end
12 |
13 | update("global/base/util");
14 | update("global/base/load_folder");
15 | TRACE("??????????????")
16 |
17 | local function main()
18 | LOAD_FOLDER("global/include");
19 | LOAD_FOLDER("global/base", "util");
20 | LOAD_FOLDER("global/inherit");
21 | LOAD_FOLDER("global/daemons", "importd:dbd:sqld:datad");
22 | LOAD_FOLDER("global/clone");
23 |
24 | LOAD_FOLDER("etc")
25 |
26 | local load_table={
27 | "user",
28 | }
29 | set_need_load_data_num(SIZEOF(load_table) )
30 |
31 | LOAD_FOLDER("share")
32 |
33 | LOAD_FOLDER("client/global")
34 | LOAD_FOLDER("client/clone");
35 | update("client/daemons/logind")
36 | update("client/daemons/med")
37 | update("client/daemons/stress_testd")
38 | -- LOAD_FOLDER("client/daemons", ""); --,"propertyd" 强制加载优先顺序
39 |
40 | -- STRESS_TEST_D.start(500, "CHAT_TESTD")
41 |
42 | LOAD_FOLDER("client/msgs")
43 |
44 | update("client/command")
45 |
46 | send_debug_on(0)
47 | debug_on(0)
48 | post_init()
49 | START_COMMAND_INPUT()
50 | print("------------------welcome to rust lua game client------------------")
51 | end
52 |
53 |
54 | local status, msg = xpcall(main, ERROR_HANDLE)
55 | if not status then
56 | print(msg)
57 | end
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | tunm
2 | ==========
3 |
4 | a game server for rust + lua
5 |
6 | [](https://travis-ci.org/tickbh/tunm)
7 |
8 |
9 | ## How to run
10 |
11 | ```
12 | git clone https://github.com/tickbh/tunm.git
13 | cd tunm
14 | cargo build
15 | ```
16 |
17 | ##dependence
18 | * redis server
19 | * mysql server
20 |
21 | and then modify config/Gate_GlobalConfig.conf and config/Client_GlobalConfig.conf to config your mysql db info, and redis db info
22 |
23 | Run these in different console
24 |
25 | ```
26 | cargo run --example server # Launch first tunm node (Gate server) (default as the standalone option)
27 | cargo run --example client # Launch a client to connect server
28 | ```
29 |
30 | ## What is tunm?
31 | An open source server engine, the clients and server communications can through the td_ptotocol.
32 | Now only has the console client.
33 |
34 | Engine framework written using Rust, game logic layer using Lua(Support the hotfix),
35 | developers do not need to re-implement some common server-side technology,
36 | allows developers to concentrate on the game logic development, quickly create a variety of games.
37 |
38 | (tunm is designed to be multi-process distributed dynamic load balancing scheme,
39 | in theory only need to expand hardware can increase load-limit, the single machine load-limit
40 | depends on complexity of logic of the game itself.)he game itself.)
41 |
42 | ## How To Use (Sorry, Only in Chinese now)
43 |
44 | Read Wiki https://github.com/tickbh/tunm/wiki
45 |
46 | ## 中文
47 |
48 | QQ交流群:432216192
--------------------------------------------------------------------------------
/src/protocol/proto_bin.rs:
--------------------------------------------------------------------------------
1 | use tunm_proto::*;
2 | use td_rlua::{self, LuaPush};
3 | use super::EngineProtocol;
4 | use {NetMsg, MSG_TYPE_BIN};
5 | use {NetResult, LuaWrapperValue};
6 | use LuaUtils;
7 |
8 | pub struct ProtoBin;
9 |
10 | impl EngineProtocol for ProtoBin {
11 | fn pack_protocol(lua: *mut td_rlua::lua_State, index: i32) -> Option {
12 | unsafe {
13 | let name: String = unwrap_or!(td_rlua::LuaRead::lua_read_at_position(lua, index), return None);
14 | if td_rlua::lua_isstring(lua, index + 1) == 0 {
15 | return None;
16 | }
17 |
18 | let val = unwrap_or!(LuaUtils::read_str_to_vec(lua, index + 1), return None);
19 | let net_msg = NetMsg::new_by_detail(MSG_TYPE_BIN, name, &val[..]);
20 | Some(net_msg)
21 | }
22 | }
23 |
24 | fn unpack_protocol(lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
25 | net_msg.set_read_data();
26 | let name: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
27 | let raw: Value = decode_str_raw(net_msg.get_buffer(), TYPE_RAW)?;
28 | name.push_to_lua(lua);
29 | LuaWrapperValue(raw).push_to_lua(lua);
30 | return Ok(2);
31 | }
32 |
33 |
34 | fn convert_string(_: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
35 | net_msg.set_read_data();
36 | let _: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
37 | let raw: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
38 | return Ok(raw);
39 | }
40 | }
--------------------------------------------------------------------------------
/src/protocol/proto_text.rs:
--------------------------------------------------------------------------------
1 | use tunm_proto::*;
2 | use td_rlua::{self, LuaPush};
3 | use super::EngineProtocol;
4 | use {NetMsg, MSG_TYPE_TEXT};
5 | use {NetResult, LuaWrapperValue};
6 |
7 | pub struct ProtoText;
8 |
9 | impl EngineProtocol for ProtoText {
10 | fn pack_protocol(lua: *mut td_rlua::lua_State, index: i32) -> Option {
11 | unsafe {
12 | let name: String = unwrap_or!(td_rlua::LuaRead::lua_read_at_position(lua, index), return None);
13 | if td_rlua::lua_isstring(lua, index + 1) == 0 {
14 | return None;
15 | }
16 | let text: String = unwrap_or!(td_rlua::LuaRead::lua_read_at_position(lua, index + 1), return None);
17 | let net_msg = NetMsg::new_by_detail(MSG_TYPE_TEXT, name, &text.as_bytes()[..]);
18 | Some(net_msg)
19 | }
20 | }
21 |
22 | fn unpack_protocol(lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
23 | net_msg.set_read_data();
24 | let name: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
25 | let raw: Value = decode_str_raw(net_msg.get_buffer(), TYPE_RAW)?;
26 | name.push_to_lua(lua);
27 | LuaWrapperValue(raw).push_to_lua(lua);
28 | return Ok(2);
29 | }
30 |
31 | fn convert_string(_: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
32 | net_msg.set_read_data();
33 | let _: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
34 | let raw: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
35 | return Ok(raw);
36 | }
37 | }
--------------------------------------------------------------------------------
/scripts/share/net.lua:
--------------------------------------------------------------------------------
1 | CMD_INTERNAL_AUTH = "cmd_internal_auth"
2 | CMD_LOGIN = "cmd_login"
3 | CMD_USER_LIST = "cmd_user_list"
4 | MSG_USER_LIST = "msg_user_list"
5 | CMD_CREATE_USER = "cmd_create_user"
6 | MSG_CREATE_USER = "msg_create_user"
7 | CMD_SELECT_USER = "cmd_select_user"
8 | MSG_ENTER_GAME = "msg_enter_game"
9 | MSG_LOGIN_NOTIFY_STATUS = "msg_login_notify_status"
10 | NEW_CLIENT_INIT = "new_client_init"
11 | LOSE_CLIENT = "lose_client"
12 | CMD_COMMON_OP = "cmd_common_op"
13 | MSG_COMMON_OP = "msg_common_op"
14 | MSG_OBJECT_UPDATED = "msg_object_updated"
15 | MSG_PROPERTY_LOADED = "msg_property_loaded"
16 | MSG_BONUS = "msg_bonus"
17 | CMD_SALE_OBJECT = "cmd_sale_object"
18 | MSG_SALE_OBJECT = "msg_sale_object"
19 | MSG_PROPERTY_DELETE = "msg_property_delete"
20 | CMD_CHAT = "cmd_chat"
21 | MSG_CHAT = "msg_chat"
22 | MSG_WAIT_QUEUE_NUMBER = "msg_wait_queue_number"
23 | CMD_ENTER_ROOM = "cmd_enter_room"
24 | MSG_ENTER_ROOM = "msg_enter_room"
25 | CMD_LEAVE_ROOM = "cmd_leave_room"
26 | MSG_LEAVE_ROOM = "msg_leave_room"
27 | CMD_ROOM_MESSAGE = "cmd_room_message"
28 | MSG_ROOM_MESSAGE = "msg_room_message"
29 | CMD_ROOM_OPER = "cmd_room_oper"
30 | MSG_ROOM_OPER = "msg_room_oper"
31 | RESPONE_ROOM_MESSAGE = "respone_room_message"
32 |
33 |
34 | MSG_DB_RESULT = "msg_db_result"
--------------------------------------------------------------------------------
/scripts/server/daemons/chatd.lua:
--------------------------------------------------------------------------------
1 | -- chatd.lua
2 | -- Created by wugd
3 | -- 负责聊天相关的模块
4 | CHAT_D = {}
5 | setmetatable(CHAT_D, {__index = _G})
6 | local _ENV = CHAT_D
7 |
8 | --[[
9 | {
10 | chat_channel = xx, send_rid = xx, recv_rid = xx, send_name = xx, chat_info = {send_content = xx, send_time = os.time()}
11 |
12 | CHAT_CHANNEL_WORLD = { {user_data = xx, chat_info = xx} },
13 | CHAT_CHANNEL_UNION = { {user_data = xx, chat_info = xx} },
14 | CHAT_CHANNEL_PRIVATE = { rid = { {user_data = xx, chat_info = xx} } },
15 | }
16 | --]]
17 |
18 | function deal_with_new_chat(data)
19 | if data.chat_channel == CHAT_CHANNEL_WORLD then
20 | local ret_msg = pack_message(get_common_msg_type(), MSG_CHAT, data.chat_channel, data)
21 | local users = USER_D.get_user_list()
22 | for _,user in pairs(users) do
23 | user:send_net_msg(ret_msg)
24 | end
25 | end
26 | end
27 |
28 | function send_system_chat(content, ext_data)
29 | local data = {chat_channel = CHAT_CHANNEL_WORLD, send_rid = GLOABL_RID, send_name = "tunm", send_rid = GLOABL_RID, chat_info = {send_content = content, send_time = os.time()}}
30 | MERGE(data.chat_info, ext_data or {})
31 | REDIS_D.run_command("PUBLISH", REDIS_CHAT_CHANNEL_WORLD, ENCODE_JSON(data))
32 | end
33 |
34 | function send_system_private_chat( rid, content, ext_data )
35 | local data = {chat_channel = CHAT_CHANNEL_PRIVATE, send_rid = GLOABL_RID, recv_rid = rid, send_name = "tunm", send_rid = GLOABL_RID, chat_info = {send_content = content, send_time = os.time()}}
36 | MERGE(data.chat_info, ext_data or {})
37 | REDIS_D.run_command("PUBLISH", string.format(CREATE_CHAT_CHANNEL_PRIVATE, rid), ENCODE_JSON(data))
38 | end
--------------------------------------------------------------------------------
/src/protocol/proto_json.rs:
--------------------------------------------------------------------------------
1 | use tunm_proto::*;
2 | use td_rlua::{self, Lua, LuaPush};
3 | use super::EngineProtocol;
4 | use {NetMsg, MSG_TYPE_JSON};
5 | use {NetResult, LuaWrapperValue};
6 |
7 | pub struct ProtoJson;
8 |
9 | impl EngineProtocol for ProtoJson {
10 | ///depend lua function arg_to_encode
11 | fn pack_protocol(lua: *mut td_rlua::lua_State, index: i32) -> Option {
12 | unsafe {
13 | for i in 1 .. index {
14 | td_rlua::lua_remove(lua, i);
15 | }
16 |
17 | let name: String = unwrap_or!(td_rlua::LuaRead::lua_read_at_position(lua, 1), return None);
18 |
19 | let mut lua = Lua::from_existing_state(lua, false);
20 | let json: String = unwrap_or!(lua.exec_func("arg_to_encode"), return None);
21 | let net_msg = NetMsg::new_by_detail(MSG_TYPE_JSON, name, &json.as_bytes()[..]);
22 | Some(net_msg)
23 | }
24 | }
25 |
26 | fn unpack_protocol(lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
27 | net_msg.set_read_data();
28 | let name: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
29 | let raw: Value = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?;
30 | name.push_to_lua(lua);
31 | LuaWrapperValue(raw).push_to_lua(lua);
32 | return Ok(2);
33 | }
34 |
35 | fn convert_string(_: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
36 | net_msg.set_read_data();
37 | let _: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
38 | let raw: String = decode_str_raw(net_msg.get_buffer(), TYPE_STR)?.into();
39 | return Ok(raw);
40 | }
41 | }
--------------------------------------------------------------------------------
/scripts/server/daemons/user_redisd.lua:
--------------------------------------------------------------------------------
1 | -- user_redisd.lua
2 | -- create by wugd
3 | -- 缓存数据获取,一切非自己的数据取皆为异步
4 | USER_REDISD = {}
5 | setmetatable(USER_REDISD, {__index = _G})
6 | local _ENV = USER_REDISD
7 |
8 | cookie_map = {}
9 | local use_serialize = true
10 |
11 | local function check_finish(data)
12 | if data["readnum"] > 0 and not data.failed then
13 | return
14 | end
15 |
16 | if not data["time"] or not data["upload"] then
17 | data.failed = true
18 | REDIS_D.run_command("DEL", data.rid)
19 | end
20 |
21 | local record = cookie_map[data.cookie]
22 | if not record then
23 | return
24 | end
25 | cookie_map[data.cookie] = nil
26 | record.callback(data, record.callback_arg)
27 | end
28 |
29 | local function accout_user_callback(data, result_list)
30 | data["readnum"] = data["readnum"] - 1
31 | if not REDIS_D.check_string(result_list) then
32 | data.failed = true
33 | else
34 | MERGE(data, DECODE_JSON(result_list))
35 | end
36 | check_finish(data)
37 | end
38 |
39 | function load_data_from_db(rid, callback, callback_arg)
40 | ASSERT(callback ~= nil and type(callback) == "function", "callback must not empty")
41 | local data = { rid = rid, cookie = new_cookie(), readnum = 1, is_redis = true }
42 | local record = {
43 | callback = callback,
44 | callback_arg = callback_arg,
45 | }
46 | cookie_map[data.cookie] = record
47 | REDIS_D.run_command_with_call(accout_user_callback, data, "GET", rid)
48 | end
49 |
50 | function cache_data_to_db(rid, data)
51 | local ser = ENCODE_JSON(data)
52 | REDIS_D.run_command("SET", rid, ser)
53 | REDIS_D.run_command("EXPIRE", rid, CACHE_EXPIRE_TIME_REDIS)
54 | end
--------------------------------------------------------------------------------
/scripts/global/daemons/base64d.lua:
--------------------------------------------------------------------------------
1 |
2 | BASE64_D = {}
3 | setmetatable(BASE64_D, {__index = _G})
4 | local _ENV = BASE64_D
5 |
6 | local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
7 |
8 | -- encoding
9 | function enc(data)
10 | return ((data:gsub('.', function(x)
11 | local r,b='',x:byte()
12 | for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
13 | return r;
14 | end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
15 | if (#x < 6) then return '' end
16 | local c=0
17 | for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
18 | return b:sub(c+1,c+1)
19 | end)..({ '', '==', '=' })[#data%3+1])
20 | end
21 |
22 | -- decoding
23 | function dec(data)
24 | data = string.gsub(data, '[^'..b..'=]', '')
25 | return (data:gsub('.', function(x)
26 | if (x == '=') then return '' end
27 | local r,f='',(b:find(x)-1)
28 | for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
29 | return r;
30 | end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
31 | if (#x ~= 8) then return '' end
32 | local c=0
33 | for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
34 | return string.char(c)
35 | end))
36 | end
37 |
38 |
39 | function url_decode(str)
40 | str = string.gsub (str, "+", " ")
41 | str = string.gsub (str, "%%(%x%x)",
42 | function(h) return string.char(tonumber(h,16)) end)
43 | str = string.gsub (str, "\r\n", "\n")
44 | return str
45 | end
46 |
47 | function url_encode(str)
48 | if (str) then
49 | str = string.gsub (str, "\n", "\r\n")
50 | str = string.gsub (str, "([^%w %-%_%.%~])",
51 | function (c) return string.format ("%%%02X", string.byte(c)) end)
52 | str = string.gsub (str, " ", "+")
53 | end
54 | return str
55 | end
56 |
57 |
--------------------------------------------------------------------------------
/scripts/server/daemons/redis_scriptd.lua:
--------------------------------------------------------------------------------
1 | -- redis_scriptd.lua
2 | -- Created by wugd
3 | -- 负责竞技场相关的功能模块
4 |
5 | -- 声明模块名
6 | REDIS_SCRIPTD = {}
7 | setmetatable(REDIS_SCRIPTD, {__index = _G})
8 | local _ENV = REDIS_SCRIPTD
9 |
10 | script_folder = "server/redis_scripts/"
11 | cache_hashs = {}
12 | cache_full_path = {}
13 | script_slot = {}
14 |
15 | function load_script(name, slot)
16 | local hash_value = load_redis_script(GET_FULL_PATH(script_folder .. name .. ".lua"), "")
17 | if not hash_value or hash_value == "" then
18 | return
19 | end
20 | cache_full_path[name] = GET_FULL_PATH(script_folder .. name .. ".lua")
21 | cache_hashs[name] = hash_value
22 | script_slot[name] = slot
23 | return true
24 | end
25 |
26 | function reload_redis_scripts()
27 | local files = GET_FLODER_FILES(script_folder)
28 | for _,v in pairs(files) do
29 | local name = string.sub(v, string.len(script_folder) + 2)
30 | load_script(name)
31 | end
32 | end
33 |
34 | local function callback_eval_script(data, result_list)
35 | data.callback(data.callback_arg, result_list)
36 | end
37 |
38 | function eval_script_by_name(name, ext_data, callback, callback_arg)
39 | if not cache_hashs[name] then
40 | LOG.err("warning: in main thread load script %o", name)
41 | load_script(name)
42 | if not cache_hashs[name] then
43 | LOG.err("error: in main thread load script %o and failed!!!", name)
44 | callback({success = 0}, callback_arg)
45 | return
46 | end
47 | end
48 |
49 | REDIS_D.run_script_with_call(callback_eval_script, {callback = callback, callback_arg = callback_arg}, cache_full_path[name], cache_hashs[name], script_slot[name] or "", unpack(ext_data))
50 | end
51 |
52 | local function create()
53 | -- reload_redis_scripts()
54 | end
55 |
56 | create()
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | extern crate libc;
2 | extern crate net2;
3 | extern crate crypto;
4 | extern crate mysql;
5 | extern crate tiny_http;
6 | extern crate sys_info;
7 | extern crate url;
8 | extern crate time;
9 | extern crate rusqlite;
10 | extern crate ws;
11 | extern crate chrono;
12 |
13 | extern crate td_rlua;
14 | extern crate tunm_proto;
15 | extern crate td_rredis;
16 | extern crate td_rthreadpool;
17 | extern crate luacjson;
18 | extern crate luasocket;
19 | extern crate psocket;
20 | extern crate mio;
21 | extern crate tunm_timer;
22 | extern crate serde;
23 | extern crate serde_yaml;
24 | extern crate rand;
25 |
26 | #[macro_use] extern crate log;
27 |
28 | mod macros;
29 | mod values;
30 | mod db;
31 | mod utils;
32 | mod lua_engine;
33 | mod rp_wrapper;
34 | mod global_config;
35 | mod redis_wrapper;
36 | mod lua_custom;
37 | mod net;
38 | mod mgr;
39 | mod protocol;
40 | mod game;
41 |
42 | pub use global_config::GlobalConfig;
43 | pub use db::{DbTrait, DbMysql, DbPool, PoolTrait, RedisPool};
44 | pub use values::{ErrorKind, NetResult, make_extension_error};
45 | pub use utils::{FileUtils, TimeUtils, ThreadUtils, NetUtils, TelnetUtils, LogUtils, log_utils, LuaUtils};
46 | pub use rp_wrapper::{LuaWrapperValue, LuaWrapperVecValue, LuaWrapperTableValue};
47 | pub use redis_wrapper::{RedisWrapperResult, RedisWrapperCmd, RedisWrapperMsg,
48 | RedisWrapperVecVec};
49 | pub use lua_engine::LuaEngine;
50 | pub use mgr::{HttpMgr, CommandMgr, MioEventMgr, ProtocolMgr, WebSocketMgr, TcpMgr, WebsocketClient};
51 | pub use lua_custom::register_custom_func;
52 | pub use net::{NetMsg, AsSocket, SocketEvent, AcceptCb, ReadCb, WriteCb, EndCb, MSG_TYPE_TD, MSG_TYPE_JSON, MSG_TYPE_BIN, MSG_TYPE_TEXT};
53 | pub use protocol::{EngineProtocol, ProtoRt, ProtoJson, ProtoBin, ProtoText};
54 | pub use game::{MaJiang, KindItem};
55 |
56 | pub use td_rthreadpool::ThreadPool;
57 |
--------------------------------------------------------------------------------
/examples/client.rs:
--------------------------------------------------------------------------------
1 | extern crate tunm;
2 |
3 | use std::thread;
4 |
5 | use tunm::{GlobalConfig, LuaEngine, register_custom_func, MioEventMgr, FileUtils, DbPool, RedisPool};
6 |
7 | use std::env;
8 |
9 | fn main() {
10 | log4rs::init_file("config/log4rs.yml", Default::default()).unwrap();
11 | let args = env::args();
12 | for arg in args {
13 | println!("args {:?}", arg);
14 | }
15 |
16 | let success = GlobalConfig::change_by_file("config/Client_GlobalConfig.conf");
17 | assert_eq!(success, true);
18 |
19 | let global_config = GlobalConfig::instance();
20 | assert_eq!(success, true);
21 |
22 | let success = DbPool::instance().set_db_info(global_config.db_info.clone());
23 | assert_eq!(success, true);
24 |
25 | let success = RedisPool::instance().set_url_list(global_config.get_redis_url_list());
26 | assert_eq!(success, true);
27 |
28 | let lua = LuaEngine::instance().get_lua();
29 | for (key, value) in &global_config.lua_macros {
30 | let value = &**value;
31 | if let Some(i) = value.trim().parse::().ok() {
32 | lua.set(&**key, i);
33 | } else if value.trim() == "true" {
34 | lua.set(&**key, true);
35 | } else if value.trim() == "false" {
36 | lua.set(&**key, false);
37 | } else {
38 | lua.set(&**key, value);
39 | }
40 | }
41 |
42 | FileUtils::instance().add_search_path("scripts/");
43 |
44 | register_custom_func(lua);
45 | let _ : Option<()> = LuaEngine::instance().get_lua().exec_string(format!("require '{:?}'", global_config.start_lua));
46 | MioEventMgr::instance().add_lua_excute();
47 |
48 | thread::spawn(move || {
49 | let _ = MioEventMgr::instance().run_server();
50 | });
51 |
52 | let _ = MioEventMgr::instance().run_timer();
53 |
54 |
55 | println!("Finish Server!");
56 | }
57 |
--------------------------------------------------------------------------------
/scripts/data/txt/attrib_formula.txt:
--------------------------------------------------------------------------------
1 | # 对象属性公式表
2 | #属性 对象类型 公式 备注
3 | string string string string
4 | attrib ob_type formula desc
5 | attack OB_TYPE_USER CALC_USER_ATTACK_ATTRIB 计算角色最终的物理攻击力
6 | magic_attack OB_TYPE_USER CALC_USER_MAGIC_ATTACK_ATTRIB 计算角色最终的技能攻击力
7 | defense OB_TYPE_USER CALC_USER_DEFENSE_ATTRIB 计算角色最终的防御力
8 | critical OB_TYPE_USER CALC_USER_CRITICAL_ATTRIB 计算角色最终的暴击力概率
9 | critical_damage OB_TYPE_USER CALC_USER_CRITICAL_DAMAGE_ATTRIB 计算暴击的伤害效果
10 | halo_power OB_TYPE_USER CALC_USER_HALO_POWER_ATTRIB 计算角色最终的光辉之力
11 | max_hp OB_TYPE_USER CALC_USER_MAX_HP 计算角色最终的气血上限
12 | critical_reduce OB_TYPE_USER CALC_USER_CRITICAL_REDUCE_ATTRIB 角色暴击抵抗
13 | damage_reduce OB_TYPE_USER CALC_USER_DAMAGE_REDUCE_ATTRIB 伤害减免
14 | defense_reduce OB_TYPE_USER CALC_USER_DEFENSE_REDUCE_ATTRIB 破甲
15 | cd_reduce OB_TYPE_USER CALC_USER_COOLDOWN_REDUCE 冷却缩减
16 | hp_recover OB_TYPE_USER CALC_USER_HP_RECOVER_ATTRIB 气血回复
17 | attack_recover OB_TYPE_USER CALC_USER_ATTACK_RECOVER_ATTRIB 攻击吸血
18 | skill OB_TYPE_USER CALC_USER_SKILL_ATTRIB
19 | speed OB_TYPE_USER CALC_USER_SPEED_ATTRIB 移动速度
20 | status OB_TYPE_USER CALC_STATUS_ATTRIB
21 | status_cond OB_TYPE_USER CALC_STATUS_COND_ATTRIB
22 | room_pos OB_TYPE_USER CALC_USER_ROOM_POS_ATTRIB 当前坐标
23 | assign_daily_limit OB_TYPE_USER CALC_USER_ASSIGN_DAILY_LIMIT 悬赏令任务最大接取数
24 | attack OB_TYPE_EQUIP CALC_EQUIP_ATTRIB 计算装备的6个属性
25 | magic_attack OB_TYPE_EQUIP CALC_EQUIP_ATTRIB 计算装备的6个属性
26 | max_hp OB_TYPE_EQUIP CALC_EQUIP_ATTRIB 计算装备的6个属性
27 | defense OB_TYPE_EQUIP CALC_EQUIP_ATTRIB 计算装备的6个属性
28 | critical OB_TYPE_EQUIP CALC_EQUIP_ATTRIB 计算装备的6个属性
29 | enya_power OB_TYPE_EQUIP CALC_EQUIP_ATTRIB 计算装备的6个属性
30 | level OB_TYPE_EQUIP CALC_EQUIP_LEVEL 计算装备的6个属性
31 | color OB_TYPE_EQUIP CALC_EQUIP_COLOR 计算装备的颜色
32 | name OB_TYPE_EQUIP CALC_EQUIP_NAME 装备名字
33 | adventure_limit OB_TYPE_USER CALC_ADVENTURE_MAX_TIME_PER_DAY 考古系统每天次数限制
34 | pet_max_exp OB_TYPE_USER CALC_MAX_PETEXP_STORE_VALUE 计算灵魂结晶上限
35 | adventure_times OB_TYPE_USER GET_ADVENTURE_TIMES 获取玩家考古次数
36 |
--------------------------------------------------------------------------------
/scripts/global/base/load_folder.lua:
--------------------------------------------------------------------------------
1 | --load_folder.lua
2 | --Created by wugd
3 | --加载指定文件下的脚本
4 |
5 | -- 定义公共接口,按照字母顺序排序
6 |
7 | --加载path文件夹下的lua文件,arrange指定加载文件的
8 | --先后关系,如"a:b:c",将按照a、b、c的顺序加载
9 |
10 | function LOAD_FOLDER(path, arrange, ...)
11 | local table_file = GET_FLODER_FILES(path, ...)
12 | if SIZEOF(table_file) == 0 then
13 | TRACE("找不到目录(%s)下的文件", path);
14 | return;
15 | end
16 |
17 | local linestart = 1
18 | local e
19 | local s
20 | local str = {}
21 |
22 | --先加载具有先后关系的文件
23 | if arrange then
24 | repeat
25 | if string.find(arrange,":",linestart) then
26 | _,e = string.find(arrange,":",linestart)
27 | s = string.sub(arrange,linestart,e-1)
28 |
29 | for k,v in pairs(table_file) do
30 | if string.find(v,'/'.. s .. ".lua") or string.find(v,'\\'.. s .. ".lua") then
31 | update(v)
32 | str[v] = true
33 | table.remove(table_file,k)
34 | break
35 | end
36 | end
37 | linestart = e+1
38 |
39 | --处理最后一个文件
40 | else
41 | s = string.sub(arrange,linestart,string.len(arrange))
42 |
43 | for k,v in pairs(table_file) do
44 | if string.find(v,'/'..s) or string.find(v,'\\'..s) then
45 | str[v] = true
46 | update(v)
47 | table.remove(table_file,k)
48 | break
49 | end
50 | end
51 |
52 | linestart = string.len(arrange) + 1
53 | end
54 | until linestart > string.len(arrange)
55 | end
56 |
57 | --加载其他未指定先后关系的文件
58 | for k,v in pairs(table_file) do
59 | if not str[v] then
60 | str[v] = true
61 | update(v)
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/scripts/server/cmds/cmd_server.lua:
--------------------------------------------------------------------------------
1 | --cmd_server.lua
2 |
3 | --逻辑服或者网关服接收客户端登出
4 | function lose_client(agent, fd)
5 | fd = bit32.band(fd, 0xFFFF)
6 | -- if STANDALONE then
7 | -- local client_agent = find_agent_by_port(fd)
8 | -- if client_agent then
9 | -- client_agent:set_sended_close(true)
10 | -- client_agent:connection_lost()
11 | -- ASSERT(find_agent_by_port(fd) == nil, "client is must nil")
12 | -- end
13 |
14 | -- local client_agent = find_agent_by_port(fd + 0x10000)
15 | -- if client_agent then
16 | -- client_agent:set_sended_close(true)
17 | -- client_agent:connection_lost()
18 | -- ASSERT(find_agent_by_port(fd) == nil, "client is must nil")
19 | -- end
20 | -- else
21 | if SERVER_TYPE == "logic" then
22 | fd = fd + 0x10000
23 | end
24 | local client_agent = find_agent_by_port(fd)
25 | if client_agent then
26 | client_agent:set_sended_close(true)
27 | client_agent:connection_lost()
28 | ASSERT(find_agent_by_port(fd) == nil, "client is must nil")
29 | end
30 | -- end
31 | end
32 |
33 | function cmd_inner_enter_server(agent, port, data, ext)
34 | --端口区分本地端口
35 | TRACE("cmd_inner_enter_server port ==== ", port, data)
36 | port = tonumber(port) + 0x10000
37 | --断线重连
38 | local old_agent = find_agent_by_port(port)
39 | if old_agent then
40 | old_agent:connection_lost(true)
41 | end
42 | local client_agent = CLONE_OBJECT(USER_TDCLS, data);
43 | -- 设置端口与 agent 的映射关系
44 | client_agent:set_all_port_no(port, agent:get_port_no())
45 | client_agent:set_client_ip(ext["client_ip"])
46 | if ext.is_websocket then
47 | client_agent:set_websocket(ext.is_websocket)
48 | end
49 | client_agent:set_code_type(SERVER_TYPE_CLIENT, 0)
50 | client_agent:set_authed(true)
51 |
52 | client_agent:send_message(MSG_ENTER_SERVER, {status="ok"})
53 | end
--------------------------------------------------------------------------------
/scripts/global/base/game_util.lua:
--------------------------------------------------------------------------------
1 | --游戏相关的方法
2 |
3 | function get_ob_rid(ob)
4 | ASSERT(IS_OBJECT(ob), "must be a object")
5 | return ob:query("rid")
6 | end
7 |
8 | -- 生成 pos
9 | function MAKE_POS(x, y)
10 | return (string.format("%d-%d", x, y));
11 | end
12 |
13 | -- 读取 pos
14 | function READ_POS(pos)
15 | local x, y = string.match(pos, "(%d+)-(%d+)");
16 | return tonumber(x), tonumber(y);
17 | end
18 |
19 | function is_rid_vaild(rid)
20 | return string.len(rid or "") == 12
21 | end
22 |
23 | function check_rid_vaild(rid)
24 | ASSERT(is_rid_vaild(rid), "rid 必须为12位")
25 | end
26 |
27 | function set_not_in_db(ob)
28 | ASSERT(IS_OBJECT(ob), "must be an object")
29 | ob:set_temp("not_in_db", true)
30 | end
31 |
32 | function del_not_in_db(ob)
33 | ASSERT(IS_OBJECT(ob), "must be an object")
34 | ob:delete_temp("not_in_db")
35 | end
36 |
37 | function is_not_in_db(ob)
38 | ASSERT(IS_OBJECT(ob), "must be an object")
39 | return ob:query_temp("not_in_db") == true
40 | end
41 |
42 | function get_owner(ob)
43 | if IS_OBJECT(ob) then
44 | --如果没有属主,就返回自己
45 | return find_object_by_rid(ob:query("owner") or ob:GET_RID())
46 | elseif IS_TABLE(ob) then
47 | return find_object_by_rid(ob["owner"] or "")
48 | end
49 | return nil
50 | end
51 |
52 | function get_owner_rid(ob)
53 | if IS_OBJECT(ob) then
54 | --如果没有属主,就返回自己
55 | return ob:query("owner")
56 | elseif IS_TABLE(ob) then
57 | return ob["owner"]
58 | end
59 | return nil
60 | end
61 |
62 | function is_auto_rid(rid)
63 | if string.len(rid) == 17 and string.find(rid, "auto_") then
64 | return true
65 | end
66 | return false
67 | end
68 |
69 | function get_common_msg_type()
70 | return MSG_TYPE_JSON
71 | end
72 |
73 | -- 读取 pos
74 | function READ_MSG_NAME(all)
75 | local name = string.match(all, "%[%s*\"([%w_]+)\".*");
76 | return name
77 | end
78 |
79 | local test = "[ \"aaaaaafdsgf_fsd\", xxxx]"
80 | TRACE("msg name is 111111111111 %o", READ_MSG_NAME(test))
--------------------------------------------------------------------------------
/scripts/server/clone/account.lua:
--------------------------------------------------------------------------------
1 | -- user.lua
2 | -- Created by wugd
3 | -- 玩家基类
4 |
5 | ACCOUNT_TDCLS = tdcls(DBASE_TDCLS, RID_TDCLS, AGENT_TDCLS, HEARTBEAT_TDCLS, ATTRIB_TDCLS);
6 | ACCOUNT_TDCLS.name = "ACCOUNT_TDCLS";
7 |
8 | function ACCOUNT_TDCLS:create(value)
9 | ASSERT(type(value) == "table", "account::create para not corret");
10 | self:replace_dbase(value);
11 | self:freeze_dbase()
12 | self:set("ob_type", OB_TYPE_ACCOUNT);
13 |
14 | REDIS_D.run_publish(REDIS_ACCOUNT_OBJECT_CONSTRUCT, self:query("rid"))
15 | end
16 |
17 | function ACCOUNT_TDCLS:destruct()
18 | self:close_agent()
19 |
20 | REDIS_D.run_publish(REDIS_ACCOUNT_OBJECT_DESTRUCT, self:query("rid"))
21 | end
22 |
23 | -- 生成对象的唯一ID
24 | function ACCOUNT_TDCLS:get_ob_id()
25 | return (string.format("ACCOUNT_TDCLS:%s:%s", SAVE_STRING(self:query("rid")),
26 | SAVE_STRING(self:query("account"))));
27 | end
28 |
29 | function ACCOUNT_TDCLS:accept_relay(agent)
30 | agent:relay_comm(self)
31 | self:set_authed(true)
32 | end
33 |
34 | function ACCOUNT_TDCLS:set_login_user(user_ob, is_reconnect)
35 | local pre_ob = find_object_by_rid(self:query("user_rid"))
36 | if pre_ob and pre_ob:query("rid") ~= user_ob:query("rid") then
37 | pre_ob:set("account_rid", "")
38 | pre_ob:relay_comm(self)
39 | pre_ob:connection_lost(true)
40 | end
41 | user_ob:accept_relay(self, is_reconnect)
42 | user_ob:set("account_rid", self:GET_RID())
43 | self:set("user_rid", user_ob:GET_RID())
44 | end
45 |
46 | function ACCOUNT_TDCLS:get_user_ob()
47 | return find_object_by_rid(self:query("user_rid"))
48 | end
49 |
50 | -- 连接断开时不立即析构对像,供断线重连
51 | function ACCOUNT_TDCLS:connection_lost()
52 | -- 如果存在user对象,则用户由user管理
53 | local user_ob = find_object_by_rid(self:query("user_rid"))
54 | if IS_OBJECT(user_ob) then
55 | user_ob:connection_lost(true)
56 | else
57 | DESTRUCT_OBJECT(self)
58 | end
59 | end
60 |
61 | -- 取得对象类
62 | function ACCOUNT_TDCLS:get_ob_class()
63 | return "ACCOUNT_TDCLS";
64 | end
65 |
66 | function ACCOUNT_TDCLS:is_account()
67 | return true
68 | end
69 |
--------------------------------------------------------------------------------
/scripts/client/daemons/logind.lua:
--------------------------------------------------------------------------------
1 | -- logind.lua
2 | -- Created by wugd
3 | -- 登录相关模块
4 |
5 | -- 声明模块名
6 | LOGIN_D = {}
7 | setmetatable(LOGIN_D, {__index = _G})
8 | local _ENV = LOGIN_D
9 |
10 | local private_key = "wugdGame"
11 |
12 | local function connect_callback(agent, arg)
13 | if not IS_OBJECT(agent) then
14 | TRACE("连接服务器失败.")
15 | return
16 | end
17 |
18 | ME_D.set_agent(agent)
19 | TRACE("----------------------success connected server-----------------------")
20 |
21 | -- 发送验证信息
22 | agent:send_message(CMD_INTERNAL_AUTH, 2, tostring(math.random(100000, 999999)), "")
23 |
24 | local account = arg["account"]
25 | local password = arg["password"]
26 | local server_id = arg["server_id"] or 1
27 |
28 | local login_info = {}
29 | login_info["account"] = account
30 | login_info["device_id"] = arg["device_id"]
31 | --custom yourself auth func
32 | login_info["password"] = CALC_STR_MD5(password)
33 | login_info["server_id"] = server_id
34 | login_info["timestamp"] = os.time()
35 | login_info["version"] = 1
36 |
37 | -- 保存数据到agent
38 | for key, value in pairs(arg) do
39 | agent.data[key] = value;
40 | end
41 |
42 | -- 发送登录消息
43 | agent:send_message(CMD_LOGIN, login_info)
44 | end
45 |
46 | -- 登录建立连接的接口
47 | function login(account, password, extra_data)
48 | if not START_STREE_TEST and ME_D.get_agent() then
49 | return
50 | end
51 |
52 | local ip, port = GATE_IP, tonumber(GATE_CLIENT_PORT)
53 | -- 建立连接
54 | local ret = socket_connect(ip, port, 10000, connect_callback, {
55 | account = account,
56 | password = password,
57 | extra_data = extra_data,
58 | --custom yourself device id func
59 | device_id = tostring(math.random(100000, 999999)),
60 | server_id = 1,
61 | })
62 | if ret ~= 1 then
63 | -- 连接失败
64 | TRACE("连接服务器(%o:%o)失败。", ip, port)
65 | return false
66 | end
67 |
68 | return true
69 | end
70 |
71 | -- 模块的入口执行
72 | function create()
73 | end
74 |
75 | create()
76 |
--------------------------------------------------------------------------------
/scripts/client/clone/player.lua:
--------------------------------------------------------------------------------
1 | -- player.lua
2 | -- Created by wugd
3 | -- 玩家基类
4 |
5 | PLAYER_TDCLS = tdcls(DBASE_TDCLS, RID_TDCLS, AGENT_TDCLS, HEARTBEAT_TDCLS, ATTRIB_TDCLS);
6 | PLAYER_TDCLS.name = "PLAYER_TDCLS";
7 |
8 | function PLAYER_TDCLS:create(value)
9 | ASSERT(type(value) == "table", "player::create para not corret");
10 | self:replace_dbase(value);
11 | self:set("ob_type", OB_TYPE_USER);
12 | self:freeze_dbase()
13 | self.carry = {}
14 | end
15 |
16 | function PLAYER_TDCLS:destruct()
17 | for _,v in pairs(self.carry) do
18 | DESTRUCT_OBJECT(v)
19 | end
20 | end
21 |
22 | -- 生成对象的唯一ID
23 | function PLAYER_TDCLS:get_ob_id()
24 | return (string.format("PLAYER_TDCLS:%s:%s", SAVE_STRING(self:query("rid")),
25 | SAVE_STRING(self:query("account_rid"))));
26 | end
27 |
28 | -- 定义公共接口,按照字母顺序排序
29 | -- 将连接对象转接到 player 对象上
30 | function PLAYER_TDCLS:accept_relay(agent)
31 | -- 将连接转换到 player 对象上
32 | agent:relay_comm(self)
33 |
34 | self:enter_world()
35 | end
36 |
37 | -- 玩家进入世界
38 | function PLAYER_TDCLS:enter_world()
39 | self:set_temp("entered_world", true)
40 | TRACE("玩家(%o/%s)进入游戏世界。", self:query("name"), get_ob_rid(self));
41 | TRACE("玩家等级: %d\r\n玩家金币: %d\r\n玩家钻石: %d", self:query("lv"), self:query("gold"), self:query("stone"))
42 | end
43 |
44 | -- 取得对象类
45 | function PLAYER_TDCLS:get_ob_class()
46 | return "PLAYER_TDCLS";
47 | end
48 |
49 | function PLAYER_TDCLS:is_user()
50 | return true;
51 | end
52 |
53 | function PLAYER_TDCLS:load_property(object)
54 | self.carry[object:query("pos")] = object
55 | end
56 |
57 | function PLAYER_TDCLS:unload_property(object)
58 | if not IS_OBJECT(object) then
59 | return
60 | end
61 | local pos = object:query("pos")
62 | if pos then
63 | self.carry[pos] = nil
64 | end
65 | DESTRUCT_OBJECT(object)
66 | end
67 |
68 | function PLAYER_TDCLS:get_page_carry(page)
69 | local arr = {};
70 | local x, y;
71 | local read_pos = READ_POS;
72 | for pos, ob in pairs(self.carry) do
73 | x, y = read_pos(pos);
74 | if x == page then
75 | arr[#arr + 1] = ob;
76 | end
77 | end
78 | return arr;
79 | end
--------------------------------------------------------------------------------
/scripts/server/cmds/cmd_gate.lua:
--------------------------------------------------------------------------------
1 | --cmd_gate.lua
2 |
3 | -- 验证帐号命令处理
4 | function cmd_internal_auth(agent, connect_type, device_id, sign_info)
5 | agent:set_authed(true)
6 | local is_websocket = agent:is_websocket() and 1 or 0
7 | agent:send_logic_message(NEW_CLIENT_INIT, agent:get_port_no(), {}, {client_ip=agent:get_client_ip(), is_websocket = is_websocket} )
8 | end
9 |
10 | function cmd_agent_identity(agent, code_type, code_id, password)
11 | code_type, code_id = tonumber(code_type), tonumber(code_id)
12 | TRACE("cmd_agent_identity == code_type = %o, code_id = %o, password = %o", code_type, code_id, password)
13 | if code_type == SERVER_TYPE_CLIENT then
14 | agent:set_code_type(code_type, 0)
15 | agent:set_authed(true)
16 | return
17 | end
18 |
19 | local calc_password = CALC_STR_MD5(string.format("%s:%s:%s", code_type, code_id, SECRET_KEY))
20 | if calc_password ~= password then
21 | agent:connection_lost()
22 | return
23 | end
24 | agent:set_code_type(code_type, code_id)
25 | agent:set_authed(true)
26 | end
27 |
28 | function cmd_check_heart(agent)
29 | agent:send_message(MSG_CHECK_HEART, {status= "ok"})
30 | end
31 |
32 | function cmd_enter_server(agent, server)
33 | TRACE("11111111111 cmd_enter_server ==== %o", server)
34 | if not IS_TABLE(server) then
35 | return
36 | end
37 |
38 | local code_type, code_id = tonumber(server["code_type"]), tonumber(server["code_id"])
39 | if not code_type or not code_id then
40 | return
41 | end
42 |
43 | TRACE("22222222222222222222agent ==== %o", agent)
44 | if not agent:is_user() then
45 | TRACE("非USER请求进入服务")
46 | return
47 | end
48 |
49 | local server_agent = find_port_by_code(code_type, code_id)
50 | TRACE("3333333333333333333 ==== %o server_agent = %o", server, server_agent)
51 | if not IS_OBJECT(server_agent) then
52 | return
53 | end
54 |
55 | TRACE("44444444444444444 ==== %o === %o", server, agent:get_port_no())
56 | server_agent:send_dest_message({code_type=code_type, code_id=code_id}, CMD_INNER_ENTER_SERVER, agent:get_port_no(), agent:query_into_server_data(), {is_websocket=agent:is_websocket()})
57 | end
--------------------------------------------------------------------------------
/src/protocol/proto_rt.rs:
--------------------------------------------------------------------------------
1 | use td_rlua::{self, Lua, LuaPush};
2 | use tunm_proto;
3 | use super::EngineProtocol;
4 | use {NetMsg, NetUtils};
5 | use {NetResult, LuaWrapperTableValue};
6 |
7 | pub struct ProtoRt;
8 |
9 | impl EngineProtocol for ProtoRt {
10 | fn pack_protocol(lua: *mut td_rlua::lua_State, index: i32) -> Option {
11 | let name: String = unwrap_or!(td_rlua::LuaRead::lua_read_at_position(lua, index), return None);
12 | let value = NetUtils::lua_convert_value(lua, index + 1);
13 | if value.is_none() {
14 | println!("data convert failed name = {:?}", name);
15 | return None;
16 | }
17 | let value = value.unwrap();
18 | let mut net_msg = NetMsg::new();
19 | unwrap_or!(tunm_proto::encode_proto(net_msg.get_buffer(), &name, value).ok(),
20 | return None);
21 | net_msg.end_msg();
22 | if net_msg.len() > 0xFFFFFF {
23 | println!("pack message({}) size > 0xFFFF fail!", name);
24 | return None;
25 | }
26 | Some(net_msg)
27 | }
28 |
29 | fn unpack_protocol(lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
30 | net_msg.set_read_data();
31 | if let Ok((name, val)) = tunm_proto::decode_proto(net_msg.get_buffer()) {
32 | name.push_to_lua(lua);
33 | LuaWrapperTableValue(val).push_to_lua(lua);
34 | return Ok(2);
35 | } else {
36 | return Ok(0);
37 | }
38 | }
39 |
40 | fn convert_string(lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
41 | net_msg.set_read_data();
42 | if let Ok((name, val)) = tunm_proto::decode_proto(net_msg.get_buffer()) {
43 | unsafe {
44 | td_rlua::lua_settop(lua, 0);
45 | name.push_to_lua(lua);
46 | LuaWrapperTableValue(val).push_to_lua(lua);
47 | let mut lua = Lua::from_existing_state(lua, false);
48 | let json: String = unwrap_or!(lua.exec_func("arg_to_encode"), return Ok("".to_string()));
49 | return Ok(json);
50 | }
51 | } else {
52 | return Ok("".to_string());
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/scripts/global/base/log.lua:
--------------------------------------------------------------------------------
1 | --log.lua
2 | --Created by wugd
3 | --日志类相关的信息
4 | LOG = {}
5 | setmetatable(LOG, {__index = _G})
6 | local _ENV = LOG
7 |
8 | LOG_ERROR = 1
9 | LOG_WARN = 2
10 | LOG_INFO = 3
11 | LOG_DEBUG = 4
12 | LOG_TRACE = 5
13 |
14 | local function format(value, ...)
15 | local a = {...}
16 | local i = 0
17 | if(type(value) == "string") then
18 | value = string.gsub(value,"%%([o,s,d])",function(c)
19 | i = i+1
20 | if c == "s" then
21 | return a[i]
22 | else
23 | return (WATCH(a[i]))
24 | end
25 | end)
26 | end
27 |
28 | for idx = i + 1, #a do
29 | value = value .. string.format(" args : %d, value : %s", idx, WATCH(a[idx]))
30 | end
31 | return value
32 | end
33 |
34 | local function get_log_level()
35 | if LOG_LEVEL then
36 | return tonumber(LOG_LEVEL) or LOG_WARN
37 | end
38 | return LOG_WARN
39 | end
40 |
41 | function err(value, ...)
42 | if get_log_level() < LOG_ERROR then
43 | return
44 | end
45 |
46 | value = format(value, ...)
47 | LUA_PRINT(LOG_ERROR, value)
48 | end
49 |
50 | function warn(value, ...)
51 | if get_log_level() < LOG_WARN then
52 | return
53 | end
54 |
55 | value = format(value, ...)
56 | LUA_PRINT(LOG_WARN, value)
57 | end
58 |
59 | function info(value, ...)
60 | if get_log_level() < LOG_INFO then
61 | return
62 | end
63 |
64 | value = format(value, ...)
65 | LUA_PRINT(LOG_INFO, value)
66 | end
67 |
68 | function debug(value, ...)
69 | if get_log_level() < LOG_DEBUG then
70 | return
71 | end
72 |
73 | value = format(value, ...)
74 | LUA_PRINT(LOG_DEBUG, value)
75 | end
76 |
77 | function TRACE(value, ...)
78 | if get_log_level() < LOG_TRACE then
79 | return
80 | end
81 |
82 | value = format(value, ...)
83 | LUA_PRINT(LOG_TRACE, value)
84 | end
85 |
86 | if _G.TRACE then
87 | _G.TRACE = TRACE
88 | end
89 |
90 | TRACE("end!!!!!")
91 |
--------------------------------------------------------------------------------
/src/global_config.rs:
--------------------------------------------------------------------------------
1 | use serde::{Serialize, Deserialize};
2 | use std::collections::HashMap;
3 | use FileUtils;
4 | /// it will read config for file
5 | #[derive(Serialize, Deserialize, Debug)]
6 | pub struct GlobalConfig {
7 | pub lua_macros: HashMap,
8 | pub start_lua: String,
9 | pub db_info: HashMap,
10 | pub telnet_addr: Option,
11 | }
12 | static mut EL: *mut GlobalConfig = 0 as *mut _;
13 |
14 | impl GlobalConfig {
15 | pub fn instance() -> &'static GlobalConfig {
16 | unsafe {
17 | if EL == 0 as *mut _ {
18 | let config = GlobalConfig {
19 | lua_macros: HashMap::new(),
20 | db_info: HashMap::new(),
21 | start_lua: "main.lua".to_string(),
22 | telnet_addr: None,
23 | };
24 | EL = Box::into_raw(Box::new(config));
25 | }
26 | &*EL
27 | }
28 | }
29 |
30 | pub fn change_instance(file_data: &str) -> bool {
31 | let field: Result = serde_yaml::from_str(file_data);
32 | let config = unwrap_or!(field.ok(), return false);
33 | unsafe {
34 | if EL != 0 as *mut _ {
35 | let old = Box::from_raw(EL);
36 | drop(old);
37 | }
38 | EL = Box::into_raw(Box::new(config));
39 | }
40 | true
41 | }
42 |
43 | pub fn change_by_file(file_name: &str) -> bool {
44 | if let Ok(file_data) = FileUtils::get_file_data(file_name) {
45 | println!("load config file:{}", file_name);
46 | let file_data = unwrap_or!(String::from_utf8(file_data).ok(), return false);
47 | return GlobalConfig::change_instance(&*file_data);
48 | }
49 | false
50 | }
51 |
52 | pub fn get_redis_url_list(&self) -> Vec {
53 | let mut result = vec![];
54 | for i in 0..10 {
55 | let key = if i == 0 {
56 | "redis".to_string()
57 | } else {
58 | format!("redis{}", i)
59 | };
60 | if self.db_info.contains_key(&key) {
61 | result.push(self.db_info.get(&key).unwrap().clone());
62 | }
63 | }
64 | result
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/scripts/global/base/global_frame.lua:
--------------------------------------------------------------------------------
1 | -- global_frame.lua
2 | -- Created by wugd
3 | -- 每帧的处理函数
4 |
5 | local callback_info = { callback = {}, callback_count = 0 };
6 | local callback_switch = false;
7 | local cookie = 1;
8 | local timer_cache = {};
9 | setmetatable(timer_cache, { __mode = "k" });
10 |
11 | -- 定时器的回调
12 | local function timer_callback(evid, para)
13 | local callback, arg, is_repeat = para["callback"], para["arg"], para["is_repeat"];
14 | -- 调用真实的回调处理
15 | if type(callback) == "function" then
16 | if _DEBUG or callback_switch then
17 | -- 记录当前的回调函数信息
18 | callback_info["callback"] = debug.getinfo(callback, 'S');
19 | end
20 | xpcall(callback, ERROR_HANDLE, arg);
21 | end
22 | end
23 |
24 | function timer_event_dispatch(cookie)
25 | cookie = tonumber(cookie)
26 | local timer_info = timer_cache[cookie];
27 | if not timer_info then
28 | return;
29 | end
30 | timer_callback(cookie, timer_info);
31 | end
32 |
33 | function get_timer_cache()
34 | return timer_cache;
35 | end
36 |
37 | -- 取得回调函数的信息
38 | function get_callback_info()
39 | return callback_info["callback"]["short_src"], callback_info["callback"]["linedefined"], callback_info["callback_count"];
40 | end
41 |
42 | -- 设置定时器
43 | function set_timer(timeout, callback, arg, is_repeat, at_once)
44 |
45 | ASSERT(timeout > 0, "超时时间必须>0");
46 | -- 创建一个新的 timer
47 | local id = timer_event_set(timeout, is_repeat or false, at_once or false);
48 | if not id then
49 | ASSERT(false, "设置定时器失败。");
50 | end
51 |
52 | local cache_arg;
53 |
54 | cache_arg = {
55 | timeout = timeout,
56 | arg = arg,
57 | id = id,
58 | callback = callback,
59 | is_repeat = is_repeat,
60 | };
61 | -- setmetatable(cache_arg, { __mode = "v" });
62 | ASSERT(not timer_cache[id]);
63 | timer_cache[id] = cache_arg;
64 | return id;
65 | end
66 |
67 | -- 删除定时器
68 | function delete_timer(time_id)
69 | timer_cache[time_id] = nil;
70 | timer_event_del(time_id);
71 | end
72 |
73 | function get_timer(time_id)
74 | return timer_cache[time_id];
75 | end
76 |
77 | function get_all_timer()
78 | return timer_cache;
79 | end
80 |
81 | function get_timer_count()
82 | return #timer_cache;
83 | end
84 |
85 | function callback_switch_on(switch)
86 | callback_switch = switch;
87 | end
88 |
--------------------------------------------------------------------------------
/scripts/server/server.lua:
--------------------------------------------------------------------------------
1 |
2 | assert(false, "no enable")
3 | -- 更新一个文件,强制重新载入
4 | function update(name)
5 | name = string.gsub(name, ".lua", "") .. ".lua"
6 | local full_name = GET_FULL_PATH(name)
7 | package.loaded[full_name] = false
8 | require(full_name)
9 | -- 回收垃圾
10 | collectgarbage("collect")
11 | end
12 |
13 | if ENABLE_LUA_HELPER_DEBUG then
14 | -- 把luasocket添加到相关地方, 先启动luahelper
15 | require("socket.core")
16 | require("LuaPanda").start("127.0.0.1", 8818);
17 | end
18 | math.randomseed(os.time())
19 | update("global/base/util")
20 | update("global/base/load_folder")
21 |
22 | -- local socket = luaopen_socket_core()
23 | -- TRACE("socket == %o", socket)
24 | -- local socket = require("luasocket")
25 | -- TRACE("SOCKET == %o", socket)
26 |
27 | function test_env()
28 | set_port_map(1, 2)
29 | TRACE("get_port_map %o", get_port_map())
30 | hotfix_file(GET_FULL_PATH("test/fix.lua") )
31 | set_port_map(2, 3)
32 | TRACE("get_port_map %o", get_port_map())
33 | end
34 |
35 | local function main()
36 | LOAD_FOLDER("global/include")
37 | LOAD_FOLDER("global/base", "util")
38 | LOAD_FOLDER("global/inherit")
39 | LOAD_FOLDER("global/daemons", "importd:dbd:sqld:datad")
40 | LOAD_FOLDER("global/clone")
41 |
42 | LOAD_FOLDER("define")
43 |
44 | LOAD_FOLDER("etc")
45 |
46 | local load_table={
47 | "user",
48 | }
49 | set_need_load_data_num(SIZEOF(load_table) )
50 |
51 | LOAD_FOLDER("share")
52 |
53 | LOAD_FOLDER("server/clone")
54 | LOAD_FOLDER("server/clone/rooms", "room:desk")
55 | LOAD_FOLDER("server/daemons", "sqld:dbd:datad:redisd:redis_queued:redis_scriptd") --,"propertyd" 强制加载优先顺序
56 | LOAD_FOLDER("server/daemons/poker")
57 | LOAD_FOLDER("server/cmds")
58 | LOAD_FOLDER("server/msgs")
59 |
60 | --test_env()
61 | if not _DEBUG or _DUBUG == "false" then
62 | send_debug_on(0)
63 | debug_on(0)
64 | end
65 |
66 | post_init()
67 | START_COMMAND_INPUT()
68 | TRACE("------------------welcome to rust lua game server------------------")
69 |
70 | -- local msg = pack_message(MSG_TYPE_JSON, "aaaaaaaa", {a="1111", c="xxxxxxxxxx", d= {a="xxxx"}}, {b="xxxxxxxxxxx"})
71 | -- local name, un = msg_to_table(msg)
72 | -- TRACE("name = %o un = %o", name, un)
73 | end
74 |
75 |
76 | local status, msg = xpcall(main, ERROR_HANDLE)
77 | if not status then
78 | print(msg)
79 | end
--------------------------------------------------------------------------------
/scripts/server/daemons/data_userd.lua:
--------------------------------------------------------------------------------
1 | -- data_userd.lua
2 | -- Created by wugd
3 | -- 负责缓存数据相关的功能模块
4 |
5 | -- 声明模块名
6 | DATA_USERD = {}
7 | setmetatable(DATA_USERD, {__index = _G})
8 | local _ENV = DATA_USERD
9 |
10 | --缓存角色 rid,名字,竞技场区,战力,战斗详细数据
11 | user_rid_data = {}
12 | user_name_data = {}
13 |
14 | --自动玩家的数据,若没有则自动生成
15 | auto_user_rid_data = {}
16 |
17 | local function callback_load_user_data(startPos, ret, result_list)
18 | if ret ~= 0 then
19 | WARN("load user data failed:%o", result_list)
20 | return
21 | end
22 | for _,value in ipairs(result_list) do
23 | user_rid_data[value.rid] = value
24 | user_name_data[value.name] = value
25 | end
26 | if #result_list == 10000 then
27 | load_user_data_from_db(startPos + 10000)
28 | else
29 | finish_one_load_data()
30 | end
31 | end
32 |
33 | function user_info_change(rid, data)
34 | local value = user_rid_data[rid]
35 | MERGE(value, data)
36 | end
37 |
38 | function load_user_data_from_db(startPos)
39 | startPos = startPos or 0
40 | local sql = SQL_D.select_sql("user", {_FIELDS= {"rid", "name", "ban_flag", "lv", "vip", "last_login_time", "last_logout_time"},
41 | _LIMIT=10000, _OFFSET=startPos } )
42 | DB_D.read_db("user", sql, callback_load_user_data, startPos)
43 | end
44 |
45 | function user_data_changed(info)
46 | local rid = REMOVE_GET(info, "rid")
47 | if not rid then
48 | return
49 | end
50 | local data = get_data_by_rid( rid )
51 | if not data then
52 | data = DUP(info)
53 | user_rid_data[rid] = info
54 | end
55 | if info.name then
56 | user_name_data[data.name] = nil
57 | user_name_data[info.name] = data
58 | end
59 | MERGE(data, info)
60 | end
61 |
62 | function is_rid_online(rid)
63 | local data = user_rid_data[rid]
64 | return data and data.online == 1
65 | end
66 |
67 | function get_data_by_rid(rid)
68 | return user_rid_data[rid]
69 | end
70 |
71 | function get_data_by_name(name)
72 | return user_name_data[name]
73 | end
74 |
75 | function get_name_by_rid(rid)
76 | local data = get_data_by_rid(rid) or {}
77 | return data.name
78 | end
79 |
80 | function get_rid_by_name(name)
81 | local data = get_data_by_name(name) or {}
82 | return data.rid
83 | end
84 |
85 | function get_user_rid_data()
86 | return user_rid_data
87 | end
88 |
89 | local function create()
90 | load_user_data_from_db()
91 | end
92 |
93 | create()
--------------------------------------------------------------------------------
/src/mgr/protocol_mgr.rs:
--------------------------------------------------------------------------------
1 | use td_rlua::{self};
2 | use {EngineProtocol, ProtoRt, ProtoJson, ProtoBin, ProtoText};
3 | use {NetMsg, MSG_TYPE_TD, MSG_TYPE_BIN, MSG_TYPE_TEXT, MSG_TYPE_JSON, NetResult};
4 |
5 | #[allow(dead_code)]
6 | pub struct ProtocolMgr {
7 | td: ProtoRt,
8 | bin: ProtoBin,
9 | json: ProtoJson,
10 | text: ProtoText,
11 | }
12 |
13 | static mut EL: *mut ProtocolMgr = 0 as *mut _;
14 | impl ProtocolMgr {
15 | pub fn instance() -> &'static mut ProtocolMgr {
16 | unsafe {
17 | if EL == 0 as *mut _ {
18 | EL = Box::into_raw(Box::new(ProtocolMgr::new()));
19 | }
20 | &mut *EL
21 | }
22 | }
23 |
24 | pub fn new() -> ProtocolMgr {
25 | ProtocolMgr {
26 | td: ProtoRt {},
27 | bin: ProtoBin {},
28 | json: ProtoJson {},
29 | text: ProtoText {},
30 | }
31 | }
32 |
33 | pub fn pack_protocol(&mut self, lua: *mut td_rlua::lua_State, index: i32, msg_type: u8) -> Option {
34 | match msg_type {
35 | MSG_TYPE_TD => ProtoRt::pack_protocol(lua, index),
36 | MSG_TYPE_JSON => ProtoJson::pack_protocol(lua, index),
37 | MSG_TYPE_BIN => ProtoBin::pack_protocol(lua, index),
38 | MSG_TYPE_TEXT => ProtoText::pack_protocol(lua, index),
39 | _ => None
40 | }
41 | }
42 |
43 | pub fn unpack_protocol(&mut self, lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> i32 {
44 | let msg_type = net_msg.get_msg_type();
45 | let ret = match msg_type {
46 | MSG_TYPE_TD => ProtoRt::unpack_protocol(lua, net_msg),
47 | MSG_TYPE_JSON => ProtoJson::unpack_protocol(lua, net_msg),
48 | MSG_TYPE_BIN => ProtoBin::unpack_protocol(lua, net_msg),
49 | MSG_TYPE_TEXT => ProtoText::unpack_protocol(lua, net_msg),
50 | _ => Ok(0)
51 | };
52 | ret.ok().unwrap_or(0)
53 | }
54 |
55 |
56 | pub fn convert_string(&mut self, lua: *mut td_rlua::lua_State, net_msg: &mut NetMsg) -> NetResult {
57 | let msg_type = net_msg.get_msg_type();
58 | let ret = match msg_type {
59 | MSG_TYPE_TD => ProtoRt::convert_string(lua, net_msg),
60 | MSG_TYPE_JSON => ProtoJson::convert_string(lua, net_msg),
61 | MSG_TYPE_BIN => ProtoBin::convert_string(lua, net_msg),
62 | MSG_TYPE_TEXT => ProtoText::convert_string(lua, net_msg),
63 | _ => Ok("".to_string())
64 | };
65 | ret
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/scripts/client/daemons/med.lua:
--------------------------------------------------------------------------------
1 | -- med.lua
2 | -- Created by wugd
3 | -- 登录相关模块
4 |
5 | -- 声明模块名
6 | ME_D = {}
7 | setmetatable(ME_D, {__index = _G})
8 | local _ENV = ME_D
9 |
10 | local me_rid
11 | local me_agent
12 | local request = nil
13 | local _enter_game = false
14 |
15 | -- 进入游戏第一次更新玩家数据
16 | function me_updated(agent, data)
17 | local item_list = REMOVE_GET(data, "item_list") or {}
18 | local equip_list = REMOVE_GET(data, "equip_list") or {}
19 | -- 创建玩家
20 | local user = PLAYER_TDCLS.new(data.user)
21 | for k, v in pairs(agent.data) do
22 | user:set_temp(k, v)
23 | end
24 | -- 关联玩家对象与连接对象
25 | user:accept_relay(agent)
26 | for _,v in ipairs(item_list) do
27 | local obj = PROPERTY_D.clone_object_from(v.class_id, v)
28 | user:load_property(obj)
29 | end
30 |
31 | for _,v in ipairs(equip_list) do
32 | local obj = PROPERTY_D.clone_object_from(v.class_id, v)
33 | user:load_property(obj)
34 | end
35 |
36 | -- 设置玩家心跳
37 | user:set_heartbeat_interval(HEARTBEAT_INTERVAL)
38 | raise_issue(EVENT_LOGIN_OK, user)
39 |
40 | me_rid = get_ob_rid(user)
41 | me_agent = user
42 | end
43 |
44 | function GET_RID()
45 | return me_rid
46 | end
47 |
48 | function get_user()
49 | return (find_object_by_rid(me_rid))
50 | end
51 |
52 | function get_agent()
53 | if not IS_OBJECT(me_agent) then
54 | me_agent = nil
55 | _enter_game = false
56 | end
57 | return me_agent
58 | end
59 |
60 | function close_agent()
61 | if IS_OBJECT(me_agent) then
62 | me_agent:connection_lost()
63 | me_agent = nil
64 | end
65 | end
66 |
67 | function set_agent(agent)
68 | if not START_STREE_TEST then
69 | close_agent()
70 | end
71 | me_agent = agent
72 | end
73 |
74 | function clear_request_message()
75 | request = nil
76 | end
77 |
78 | function try_request_message()
79 | local agent = get_agent()
80 | if request ~= nil and agent ~= nil then
81 | agent:send_message(unpack(request))
82 | request = nil
83 | end
84 | end
85 |
86 | function request_message(...)
87 | local agent = get_agent()
88 | if agent ~= nil then
89 | if is_enter_game() then
90 | agent:send_message(...)
91 | else
92 | request = { ... }
93 | end
94 | else
95 | request = { ... }
96 | --LOGIN_D.login()
97 | end
98 | end
99 |
100 | function is_enter_game()
101 | return _enter_game and get_agent() ~= nil
102 | end
103 |
104 | function set_enter_game(value)
105 | _enter_game = value
106 | end
107 |
108 | -- 模块的入口执行
109 | function create()
110 | end
111 |
112 | create()
113 |
--------------------------------------------------------------------------------
/scripts/global/clone/equip.lua:
--------------------------------------------------------------------------------
1 | -- equip.lua
2 | -- Created by wugd
3 | -- 装备对象类
4 |
5 | -- 创建类模板
6 | EQUIP_TDCLS = tdcls(DBASE_TDCLS, RID_TDCLS, PROPERTY_TDCLS, ATTRIB_TDCLS)
7 | EQUIP_TDCLS.name = "EQUIP_TDCLS"
8 |
9 | -- 构造函数
10 | function EQUIP_TDCLS:create(value)
11 | ASSERT(type(value) == "table", "equip::create para not corret")
12 | ASSERT(IS_INT(value["class_id"]))
13 |
14 | --装备和装扮默认为1,在基本对象中设置
15 | value["amount"] = nil
16 | self:replace_dbase(value)
17 | end
18 |
19 | -- 析构函数
20 | function EQUIP_TDCLS:destruct()
21 | end
22 |
23 | -- 生成对象的唯一ID
24 | function EQUIP_TDCLS:get_ob_id()
25 | return (string.format("EQUIP_TDCLS:%s:%s", SAVE_STRING(self:query("rid")),
26 | SAVE_STRING(self:query("class_id"))))
27 | end
28 |
29 | -- 定义公共接口,按照字母顺序排序
30 |
31 | --获取基本类的对象
32 | function EQUIP_TDCLS:basic_object()
33 | local class_id = self.dbase["class_id"]
34 | return (find_basic_object_by_class_id(class_id))
35 | end
36 |
37 | -- 道具是否可叠加
38 | function EQUIP_TDCLS:can_combine(ob)
39 | return false
40 | end
41 |
42 | -- 道具扣除数量,返回实际扣除个数
43 | function EQUIP_TDCLS:cost_amount()
44 | -- 析构道具
45 | local owner = get_owner(self)
46 | if owner then
47 | owner:get_container():drop(self)
48 | return true
49 | end
50 | end
51 |
52 | -- 取得数据库的保存操作
53 | function EQUIP_TDCLS:get_save_oper()
54 | local oper = self:query_temp("not_in_db") and "insert" or "update"
55 |
56 | return "equip", self:query("rid"), oper
57 | end
58 |
59 | function EQUIP_TDCLS:is_equip()
60 | return true
61 | end
62 |
63 | -- 通知字段变更
64 | function EQUIP_TDCLS:notify_fields_updated(field_names)
65 | local owner = get_owner(self)
66 | if not owner then
67 | return
68 | end
69 |
70 | owner:notify_property_updated(get_ob_rid(self), field_names)
71 | end
72 |
73 | -- 取得保存数据库的信息
74 | function EQUIP_TDCLS:save_to_mapping()
75 |
76 | --insert操作,返回全部数据
77 | if self:query_temp("not_in_db") then
78 | return (self:query())
79 | end
80 |
81 | -- 道具数据发生变化的字段
82 | local change_list = self:get_change_list()
83 | local data = {}
84 |
85 | for key,_ in pairs(change_list) do
86 | if DATA_D.is_field_exist("equip", key) then
87 | data[key] = self:query(key)
88 | else
89 | return (self:query())
90 | end
91 | end
92 |
93 | if SIZEOF(data) == 0 then
94 | return
95 | end
96 |
97 | return data, 1
98 | end
99 |
100 | -- 还原dbase数据
101 | function EQUIP_TDCLS:restore_from_mapping(data, freeze)
102 | self:absorb_dbase(data)
103 |
104 | -- 设置 dbase 冻结中,若没被解冻,下线不额外进行保存
105 | if freeze then
106 | self:freeze_dbase()
107 | end
108 | end
109 |
--------------------------------------------------------------------------------
/scripts/server/daemons/cached.lua:
--------------------------------------------------------------------------------
1 | -- cached.lua
2 | -- create by wugd
3 | -- 缓存数据获取,一切非自己的数据取皆为异步
4 | CACHE_D = {}
5 | setmetatable(CACHE_D, {__index = _G})
6 | local _ENV = CACHE_D
7 |
8 | ENABLE_REDIS_CACHE = false
9 |
10 | cache_data = {}
11 |
12 | cookie_map = {}
13 |
14 | timer_map = {}
15 |
16 | local function get_cache_data(rid)
17 | local value = cache_data[rid]
18 | if not value or not value.store then
19 | return nil
20 | end
21 | if os.time() - value.store > CACHE_EXPIRE_TIME_MEMORY then
22 | cache_data[rid] = nil
23 | return nil
24 | end
25 | return value.data
26 | end
27 |
28 | function set_cache_data(rid, data)
29 | local need_cache = data.is_db or data.need_cache
30 | data.need_cache = nil
31 | data.is_db = nil
32 |
33 | cache_data[rid] = {store = os.time(), data=DUP(data)}
34 | if need_cache and ENABLE_REDIS_CACHE then
35 | USER_REDISD.cache_data_to_db(rid, data)
36 | end
37 | end
38 |
39 | function remove_cache_data(rid)
40 | cache_data[rid] = nil
41 | end
42 |
43 | local function redis_timer(rid)
44 | local map_info = timer_map[rid]
45 | if map_info == nil then
46 | return
47 | end
48 | timer_map[rid] = nil
49 | USER_DBD.load_data_from_db(rid, map_info["callback"])
50 | end
51 |
52 | local function delete_redis_timer(rid)
53 | local map_info = timer_map[rid]
54 | if map_info == nil then
55 | return
56 | end
57 | if IS_VALID_TIMER(map_info["timer_id"]) then
58 | delete_timer(map_info["timer_id"])
59 | end
60 | timer_map[rid] = nil
61 | end
62 |
63 | local function load_user_callback(data)
64 | ASSERT(data["rid"] ~= nil, "callback rid must no empty")
65 | if data.is_redis then
66 | delete_redis_timer(data["rid"])
67 | end
68 | if data.is_redis and data.failed then
69 | USER_DBD.load_data_from_db(data["rid"], load_user_callback)
70 | return
71 | end
72 |
73 | if not data.failed then
74 | set_cache_data(data["rid"], data)
75 | end
76 |
77 | local record = cookie_map[data["rid"]]
78 | if not record then
79 | return
80 | end
81 | cookie_map[data["rid"]] = nil
82 | record.callback(data, record.callback_arg)
83 | end
84 |
85 | function get_user_data(rid, callback, callback_arg)
86 | ASSERT(callback ~= nil and type(callback) == "function", "callback must not empty")
87 | local cache_data = get_cache_data(rid)
88 | if cache_data then
89 | callback(DEEP_DUP(cache_data), callback_arg)
90 | return
91 | end
92 |
93 | local record = {
94 | callback = callback,
95 | callback_arg = callback_arg,
96 | }
97 | cookie_map[rid] = record
98 | if ENABLE_REDIS_CACHE then
99 | local timer_id = set_timer(1000, redis_timer, rid, false)
100 | timer_map[rid] = {timer_id = timer_id, callback = load_user_callback}
101 | USER_REDISD.load_data_from_db(rid, load_user_callback)
102 | else
103 | USER_DBD.load_data_from_db(rid, load_user_callback)
104 | end
105 |
106 | return
107 | end
--------------------------------------------------------------------------------
/src/rp_wrapper.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 | use libc;
3 | use std::hash::Hash;
4 | use tunm_proto::Value;
5 | use td_rlua::{self, LuaPush, lua_State};
6 | /// the wrapper for push to lua
7 |
8 | #[derive(PartialEq, Clone)]
9 | pub struct LuaWrapperValue(pub Value);
10 |
11 | impl Eq for LuaWrapperValue {
12 | }
13 |
14 | impl Hash for LuaWrapperValue {
15 | fn hash(&self, state: &mut H) {
16 | core::mem::discriminant(&self.0).hash(state);
17 | }
18 | }
19 |
20 |
21 |
22 | impl LuaPush for LuaWrapperValue {
23 | fn push_to_lua(self, lua: *mut lua_State) -> i32 {
24 | match self.0 {
25 | Value::Nil => ().push_to_lua(lua),
26 | Value::Bool(val) => val.push_to_lua(lua),
27 | Value::U8(val) => val.push_to_lua(lua),
28 | Value::I8(val) => val.push_to_lua(lua),
29 | Value::U16(val) => val.push_to_lua(lua),
30 | Value::I16(val) => val.push_to_lua(lua),
31 | Value::U32(val) => val.push_to_lua(lua),
32 | Value::I32(val) => val.push_to_lua(lua),
33 | Value::U64(val) => val.push_to_lua(lua),
34 | Value::I64(val) => val.push_to_lua(lua),
35 | Value::Varint(val) => val.push_to_lua(lua),
36 | Value::Float(val) => val.push_to_lua(lua),
37 | Value::Double(val) => val.push_to_lua(lua),
38 | Value::Str(val) => val.push_to_lua(lua),
39 | Value::Raw(val) => {
40 | unsafe {
41 | td_rlua::lua_pushlstring(lua, val.as_ptr() as *const libc::c_char, val.len())
42 | };
43 | 1
44 | }
45 | Value::Map(mut val) => {
46 | let mut wrapper_val: HashMap = HashMap::new();
47 | for (k, v) in val.drain() {
48 | wrapper_val.insert(LuaWrapperValue(k), LuaWrapperValue(v));
49 | }
50 | wrapper_val.push_to_lua(lua)
51 | }
52 | Value::Arr(mut val) => {
53 | let mut wrapper_val: Vec = vec![];
54 | for v in val.drain(..) {
55 | wrapper_val.push(LuaWrapperValue(v));
56 | }
57 | wrapper_val.push_to_lua(lua)
58 | }
59 | }
60 | }
61 | }
62 |
63 | pub struct LuaWrapperVecValue(pub Vec);
64 | impl LuaPush for LuaWrapperVecValue {
65 | fn push_to_lua(mut self, lua: *mut lua_State) -> i32 {
66 | let mut index = 0;
67 | for v in self.0.drain(..) {
68 | index = LuaWrapperValue(v).push_to_lua(lua);
69 | }
70 | index
71 | }
72 | }
73 |
74 |
75 | pub struct LuaWrapperTableValue(pub Vec);
76 | impl LuaPush for LuaWrapperTableValue {
77 | fn push_to_lua(mut self, lua: *mut lua_State) -> i32 {
78 | unsafe {
79 | td_rlua::lua_newtable(lua);
80 | for (i, v) in self.0.drain(..).enumerate() {
81 | td_rlua::lua_pushnumber(lua, (i + 1) as f64);
82 | LuaWrapperValue(v).push_to_lua(lua);
83 | td_rlua::lua_settable(lua, -3);
84 | }
85 | }
86 | 1
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/utils/file_utils.rs:
--------------------------------------------------------------------------------
1 | use std::io;
2 | use std::io::prelude::*;
3 | use std::path::Path;
4 | use std::env;
5 | use std::fs::{self, File};
6 |
7 |
8 | static mut EL: *mut FileUtils = 0 as *mut _;
9 |
10 |
11 | pub struct FileUtils {
12 | search_paths: Vec,
13 | }
14 |
15 | impl FileUtils {
16 | pub fn instance() -> &'static mut FileUtils {
17 | unsafe {
18 | if EL == 0 as *mut _ {
19 | let val = FileUtils {
20 | search_paths: vec![::std::env::current_dir()
21 | .unwrap()
22 | .to_str()
23 | .unwrap()
24 | .to_string() +
25 | "/"],
26 | };
27 | EL = Box::into_raw(Box::new(val));
28 | }
29 | &mut *EL
30 | }
31 | }
32 |
33 | pub fn get_file_data(file_name: &str) -> io::Result> {
34 | let mut f = File::open(file_name)?;
35 | let mut buffer = Vec::new();
36 | // read the whole file
37 | f.read_to_end(&mut buffer)?;
38 | Ok(buffer)
39 | }
40 |
41 | pub fn get_file_str(file_name: &str) -> Option {
42 | let data = unwrap_or!(FileUtils::get_file_data(file_name).ok(), return None);
43 | let data = unwrap_or!(String::from_utf8(data).ok(), return None);
44 | return Some(data);
45 | }
46 |
47 | pub fn is_absolute_path(path: &str) -> bool {
48 | Path::new(path).is_absolute()
49 | }
50 |
51 | pub fn is_file_exists(file_name: &str) -> bool {
52 | Path::new(file_name).exists()
53 | }
54 |
55 | pub fn set_work_path(path: &str) -> bool {
56 | let root = Path::new(path);
57 | env::set_current_dir(&root).is_ok()
58 | }
59 |
60 | pub fn get_work_path() -> String {
61 | let p = env::current_dir().unwrap();
62 | p.to_str().unwrap().to_string()
63 | }
64 |
65 | pub fn full_path_for_name(&self, name: &str) -> Option {
66 | if Path::new(name).exists() {
67 | return Some(name.to_string());
68 | }
69 | for path in &self.search_paths {
70 | let new_path = path.clone() + name;
71 | if Path::new(&*new_path).exists() {
72 | return Some(new_path);
73 | }
74 | }
75 | None
76 | }
77 |
78 | pub fn add_search_path(&mut self, path: &str) {
79 | let path = path.trim_matches('\"');
80 | self.search_paths.push(path.to_string());
81 | }
82 |
83 | pub fn list_files(dir: &Path, files: &mut Vec, deep: bool) -> io::Result<()> {
84 | if fs::metadata(dir)?.is_dir() {
85 | for entry in fs::read_dir(dir)? {
86 | let entry = entry?;
87 | if fs::metadata(entry.path())?.is_dir() {
88 | if deep {
89 | let _ = Self::list_files(&entry.path(), files, deep);
90 | }
91 | } else {
92 | files.push(entry.path().to_str().unwrap().to_string());
93 | }
94 | }
95 | }
96 | Ok(())
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/scripts/global/base/raiser.lua:
--------------------------------------------------------------------------------
1 | -- raiser.lua
2 | -- Created by wugd
3 | -- 实现事件相关功能
4 |
5 | --变量定义
6 | -- event_responder 和 event_audience 格式如下:
7 | -- {
8 | -- EVENT_XXX : {
9 | -- listener = {"func" : func, args = args},
10 | -- ...
11 | -- },
12 | -- ...
13 | -- }
14 |
15 | --raiser发起事件的类
16 | --listener 接收的对像 仅做避免重复的索引用
17 | --event_routines 事件的索引
18 | local event_responder = {};
19 | local event_audience = {};
20 |
21 | -- 定义公共接口,按照字母顺序排序
22 | function get_all_audiences()
23 | return event_audience;
24 | end
25 |
26 | function get_all_responders()
27 | return event_responder;
28 | end
29 |
30 | -- 发送事件
31 | function raise_issue(event, ...)
32 | local info, f, result, ret = nil, nil, {}, nil
33 | local nodes = event_responder[event] or {}
34 | for listener, node in pairs(nodes) do
35 | f = node["func"];
36 | if type(f) == "function" then
37 | ret = CALL_FUNC(f, node["args"], ...)
38 | if ret then
39 | table.insert(result, ret)
40 | return result
41 | end
42 | end
43 | end
44 |
45 | local nodes = event_audience[event] or {}
46 | for listener, node in pairs(nodes) do
47 | f = node["func"];
48 | if type(f) == "function" then
49 | ret = CALL_FUNC(f, node["args"], ...)
50 | if ret then
51 | table.insert(result, ret)
52 | end
53 | end
54 | end
55 |
56 | return result
57 | end
58 |
59 | local function register_by_type(event_struct, listener, event_routines)
60 | -- 遍历注册的事件
61 | for event, info in pairs(event_routines) do
62 | local func = info
63 | local args = {}
64 | if type(func) ~= "function" then
65 | func = info["func"]
66 | args = info["args"]
67 | end
68 | ASSERT(type(func) == "function")
69 | event_struct[event] = event_struct[event] or {}
70 | event_struct[event][listener] = {func = func, args = args}
71 | end
72 | end
73 |
74 | -- 注册观众
75 | -- 接收指定事件,但不拦截该事件,事件会继续传递给其它观众
76 | function register_as_audience(listener, event_routines)
77 | register_by_type(event_audience, listener, event_routines)
78 | end
79 |
80 | -- 注册响应者
81 | -- 指定事件若被响应者截获,则会直接返回,不会继续传递该事件
82 | function register_as_responder(listener, event_routines)
83 | register_by_type(event_responder, listener, event_routines)
84 | end
85 |
86 | local function remove_by_type(event_struct, listener, events)
87 | ASSERT(listener ~= nil, "listener must no nil")
88 | if not events then
89 | for event,node in pairs(event_struct) do
90 | node[listener] = nil
91 | end
92 | return
93 | end
94 |
95 | for _, event in ipairs(events) do
96 | if event_struct[event] then
97 | event_struct[event][listener] = nil
98 | end
99 | end
100 | end
101 |
102 | -- 移除观众
103 | function remove_audience_from_raiser(listener, events)
104 | remove_by_type(event_audience, listener, events)
105 | end
106 |
107 | -- 移除响应者
108 | function remove_responder_from_raiser(listener, events)
109 | remove_by_type(event_responder, listener, events)
110 | end
111 |
112 |
--------------------------------------------------------------------------------
/scripts/client/msgs/msg_client.lua:
--------------------------------------------------------------------------------
1 |
2 | function msg_login_notify_status( agent, info )
3 | if info.ret and info.ret ~= 0 then
4 | TRACE("登陆时发生错误:%s", info.err_msg)
5 | end
6 | end
7 |
8 | --msg_user_list
9 | function msg_user_list(agent, list)
10 | end
11 |
12 | function msg_create_user(agent, info)
13 | end
14 |
15 | function msg_enter_game(agent, info)
16 | ME_D.me_updated(agent, info)
17 | enter_room()
18 | end
19 |
20 | function msg_common_op(user, info)
21 | end
22 |
23 | function msg_object_updated(user, rid, info)
24 | local object = find_object_by_rid(rid)
25 | if not IS_OBJECT(object) then
26 | TRACE("物件:%s不存在", rid)
27 | return
28 | end
29 |
30 | for k,v in pairs(info) do
31 | TRACE("属性更新:%o->[%o]=%o", object:query("name"), k, v)
32 | object:set(k, v)
33 | end
34 | end
35 |
36 | function msg_property_loaded(user, rid, info_list)
37 | for _,v in pairs(info_list) do
38 | local obj = PROPERTY_D.clone_object_from(v.class_id, v)
39 | user:load_property(obj)
40 | end
41 | end
42 |
43 | function msg_bonus(user, info, bonus_type)
44 | if bonus_type == BONUS_TYPE_SHOW then
45 | for _,v in pairs(info.properties or {}) do
46 | local obj = find_basic_object_by_class_id(v.class_id)
47 | ASSERT(obj, "物品不存在")
48 | TRACE("获得物品:%o,数量:%o", obj:query("name"), v.amount)
49 | end
50 | end
51 | end
52 |
53 | function msg_sale_object(user, info)
54 | if info.ret ~= 0 then
55 | TRACE("出售物品失败:%o", info.err_msg)
56 | else
57 | TRACE("出售物品成功")
58 | end
59 | end
60 |
61 | function msg_property_delete(user, rids)
62 | for _,rid in ipairs(rids) do
63 | local object = find_object_by_rid(rid)
64 | if object then
65 | TRACE("物品:%o,名称:%o消耗完毕", object:query("rid"), object:query("name"))
66 | user:unload_property(object)
67 | end
68 | end
69 | end
70 |
71 | function msg_chat( user, channel, info )
72 | info.chat_info = info.chat_info or {}
73 | if channel == CHAT_CHANNEL_WORLD then
74 | TRACE("收到来自:%s的世界聊天, 内容为:\"%s\"", info.send_name, info.chat_info.send_content)
75 | end
76 | end
77 |
78 | function msg_room_message(user, oper, info)
79 | if oper == "success_enter_room" then
80 | TRACE("成功进入房间:\"%s\"", info.room_name)
81 | user:send_message(CMD_ROOM_MESSAGE, "enter_desk", {})
82 | elseif oper == "success_enter_desk" then
83 | if info.rid == get_ob_rid(user) then
84 | desk_ready()
85 | end
86 | TRACE("%s成功进入桌子:\"%d\", 在位置:%d", info.rid, info.idx, info.wheel_idx)
87 | elseif oper == "pre_room" then
88 | if info.room_name then
89 | user:send_message(CMD_ENTER_ROOM, {room_name = info.room_name})
90 | end
91 | end
92 | end
93 |
94 |
95 |
96 | function msg_enter_room(user, info)
97 | if info.ret and info.ret < 0 then
98 | TRACE("进入房间错误:\"%s\"", info.err_msg)
99 | return
100 | end
101 | TRACE("成功进入房间:\"%s\"", info.room_name)
102 | end
103 |
104 | function msg_leave_room(user, info)
105 | if info.ret and info.ret < 0 then
106 | TRACE("离开房间错误:\"%s\"", info.err_msg)
107 | return
108 | end
109 | TRACE("成功离开房间:\"%s\"", info.room_name)
110 | end
--------------------------------------------------------------------------------
/scripts/define/server_msg.lua:
--------------------------------------------------------------------------------
1 | SERVER_TYPE_CLIENT = 0
2 | SERVER_TYPE_GATE = 1
3 | SERVER_TYPE_LOGIC = 2
4 | SERVER_TYPE_GAME = 3
5 |
6 | SERVER_NAMES = {}
7 | SERVER_NAMES[SERVER_TYPE_CLIENT] = "client"
8 | SERVER_NAMES[SERVER_TYPE_GATE] = "gate"
9 | SERVER_NAMES[SERVER_TYPE_LOGIC] = "logic"
10 | SERVER_NAMES[SERVER_TYPE_GAME] = "game"
11 |
12 | ALL_SERVER_TYPES = {}
13 | for k, _ in pairs(SERVER_NAMES) do
14 | ALL_SERVER_TYPES[#ALL_SERVER_TYPES + 1] = k
15 | end
16 |
17 | CMD_INTERNAL_AUTH = "cmd_internal_auth"
18 | CMD_AGENT_IDENTITY = "cmd_agent_identity"
19 | CMD_LOGIN = "cmd_login"
20 | CMD_ENTER_SERVER = "cmd_enter_server"
21 | CMD_INNER_ENTER_SERVER = "cmd_inner_enter_server"
22 | CMD_CHECK_HEART = "cmd_check_heart"
23 | MSG_CHECK_HEART = "msg_check_heart"
24 | CMD_USER_LIST = "cmd_user_list"
25 | MSG_USER_LIST = "msg_user_list"
26 | CMD_CREATE_USER = "cmd_create_user"
27 | MSG_CREATE_USER = "msg_create_user"
28 | CMD_SELECT_USER = "cmd_select_user"
29 | MSG_ENTER_GAME = "msg_enter_game"
30 | MSG_ENTER_SERVER = "msg_enter_server"
31 | MSG_LOGIN_NOTIFY_STATUS = "msg_login_notify_status"
32 | NEW_CLIENT_INIT = "new_client_init"
33 | LOSE_CLIENT = "lose_client"
34 | CMD_COMMON_OP = "cmd_common_op"
35 | MSG_COMMON_OP = "msg_common_op"
36 | MSG_OBJECT_UPDATED = "msg_object_updated"
37 | MSG_PROPERTY_LOADED = "msg_property_loaded"
38 | MSG_BONUS = "msg_bonus"
39 | CMD_SALE_OBJECT = "cmd_sale_object"
40 | MSG_SALE_OBJECT = "msg_sale_object"
41 | MSG_PROPERTY_DELETE = "msg_property_delete"
42 | CMD_CHAT = "cmd_chat"
43 | MSG_CHAT = "msg_chat"
44 | MSG_WAIT_QUEUE_NUMBER = "msg_wait_queue_number"
45 | CMD_ENTER_ROOM = "cmd_enter_room"
46 | MSG_ENTER_ROOM = "msg_enter_room"
47 | CMD_LEAVE_ROOM = "cmd_leave_room"
48 | MSG_LEAVE_ROOM = "msg_leave_room"
49 | CMD_ROOM_MESSAGE = "cmd_room_message"
50 | MSG_ROOM_MESSAGE = "msg_room_message"
51 | CMD_ROOM_OPER = "cmd_room_oper"
52 | MSG_ROOM_OPER = "msg_room_oper"
53 | RESPONE_ROOM_MESSAGE = "respone_room_message"
54 | MSG_DB_RESULT = "msg_db_result"
55 |
56 | MSG_DEDEAL_SERVER = {}
57 | MSG_DEDEAL_SERVER[CMD_INTERNAL_AUTH] = {SERVER_TYPE_GATE}
58 | MSG_DEDEAL_SERVER[CMD_AGENT_IDENTITY] = {SERVER_TYPE_GATE, SERVER_TYPE_LOGIC}
59 | MSG_DEDEAL_SERVER[CMD_LOGIN] = {SERVER_TYPE_GATE, SERVER_TYPE_LOGIC}
60 | MSG_DEDEAL_SERVER[CMD_CHECK_HEART] = {SERVER_TYPE_GATE}
61 | MSG_DEDEAL_SERVER[MSG_ENTER_GAME] = {SERVER_TYPE_CLIENT}
62 | MSG_DEDEAL_SERVER[MSG_ENTER_SERVER] = {SERVER_TYPE_CLIENT}
63 | MSG_DEDEAL_SERVER[LOSE_CLIENT] = ALL_SERVER_TYPES
64 | MSG_DEDEAL_SERVER[CMD_ENTER_SERVER] = {SERVER_TYPE_GATE}
65 | MSG_DEDEAL_SERVER[CMD_INNER_ENTER_SERVER] = {SERVER_TYPE_LOGIC, SERVER_TYPE_GAME}
66 |
67 |
68 | for name, value in pairs(MSG_DEDEAL_SERVER) do
69 | local new_value = {}
70 | for _, k in ipairs(value) do
71 | new_value[SERVER_NAMES[k]] = true
72 | end
73 | MSG_DEDEAL_SERVER[name] = new_value
74 | end
75 |
76 | function is_msg_can_deal(message)
77 | local info = MSG_DEDEAL_SERVER[message]
78 | if not info then
79 | return false
80 | end
81 | return info[SERVER_TYPE] or info[CODE_TYPE]
82 | end
--------------------------------------------------------------------------------
/scripts/server/clone/ddz_info.lua:
--------------------------------------------------------------------------------
1 | -- time.lua
2 | -- Created by wugd
3 | -- 玩家数据ddz_info
4 |
5 | DDZ_INFO_TDCLS = tdcls(DBASE_TDCLS, ATTRIB_TDCLS)
6 | DDZ_INFO_TDCLS.name = "DDZ_INFO_TDCLS"
7 |
8 | function DDZ_INFO_TDCLS:create(owner, value)
9 | self:init_with_data(owner, value)
10 | end
11 |
12 | function DDZ_INFO_TDCLS:init_with_data(owner, value)
13 | self.owner = owner
14 | if not value or IS_EMPTY_TABLE(value) then
15 | value = {
16 | owner = self.owner,
17 | pea_amount = 0,
18 | score = 0,
19 | win_amount = 0,
20 | lose_amount = 0,
21 | escape_amount = 0,
22 | give_times = 0,
23 | last_give_time = 0,
24 | }
25 | self:replace_dbase(value)
26 | set_not_in_db(self)
27 | else
28 | self:replace_dbase(value)
29 | end
30 | self:freeze_dbase()
31 | self:try_give_pea()
32 | end
33 |
34 | function DDZ_INFO_TDCLS:try_give_pea()
35 | --当豆豆不足1000时尝试进行赠送
36 | if self:query("pea_amount") < 1000 then
37 | local ret= is_same_day(self:query("last_give_time"))
38 | if ret ~= true then
39 | self:set("give_times", 0)
40 | self:set("last_give_time", os.time())
41 | end
42 |
43 | if self:query("give_times") < 4 then
44 | self:add("give_times", 1)
45 | self:set("last_give_time", os.time())
46 | self:add("pea_amount", 1000)
47 |
48 | return true
49 | end
50 | end
51 |
52 | return false
53 | end
54 |
55 | function DDZ_INFO_TDCLS:calc_score(info)
56 | if info.is_win == 1 then
57 | self:add("score", 6)
58 | self:add("win_amount", 1)
59 | else
60 | self:add("score", -3)
61 | self:add("lose_amount", 1)
62 | end
63 | if info.is_escape == 1 then
64 | self:add("escape_amount", 1)
65 | end
66 |
67 | local change_pea = DDZ_D.calc_pea_change(info.pea_amount_list, info.idx, info.lord_idx, info.multi_num, info.is_win)
68 | self:add("pea_amount", change_pea)
69 | if self:query("pea_amount") < 0 then
70 | self:set("pea_amount", 0)
71 | end
72 | self:try_give_pea()
73 | end
74 |
75 | -- 取得保存数据库的信息
76 | function DDZ_INFO_TDCLS:save_to_mapping()
77 | -- 道具数据发生变化的字段
78 | local change_list = self:get_change_list()
79 | local data = {}
80 | local fields = DATA_D.get_table_fields("ddz_info") or {}
81 | for key,_ in pairs(change_list) do
82 | if fields[key] then
83 | data[key] = self:query(key)
84 | end
85 | end
86 |
87 | return data
88 | end
89 |
90 | -- 取得数据库的保存路径
91 | function DDZ_INFO_TDCLS:get_save_path()
92 | return "ddz_info", { owner = self.owner }
93 | end
94 |
95 | function DDZ_INFO_TDCLS:set_change_to_db(callback, arg)
96 | local table_name, condition = self:get_save_path()
97 | if is_not_in_db(self) then
98 | local sql = SQL_D.insert_sql(table_name, self:query())
99 | arg.sql_count = arg.sql_count + 1
100 | DB_D.execute_db(table_name, sql, callback, arg)
101 | del_not_in_db(self)
102 | self:freeze_dbase()
103 | return
104 | end
105 | local dbase = self:save_to_mapping()
106 | if IS_EMPTY_TABLE(dbase) then
107 | callback(arg, 0, {})
108 | return
109 | end
110 | local sql = SQL_D.update_sql(table_name, dbase, condition)
111 | arg.sql_count = arg.sql_count + 1
112 | DB_D.execute_db(table_name, sql, callback, arg)
113 | self:freeze_dbase()
114 | end
115 |
--------------------------------------------------------------------------------
/scripts/server/daemons/user_dbd.lua:
--------------------------------------------------------------------------------
1 | -- user_dbd.lua
2 | -- create by wugd
3 | -- 缓存数据获取,一切非自己的数据取皆为异步
4 | USER_DBD = {}
5 | setmetatable(USER_DBD, {__index = _G})
6 | local _ENV = USER_DBD
7 |
8 | cookie_map = {}
9 |
10 | local function check_finish(data)
11 | if not data.failed then
12 | if data.readnum > 0 then
13 | return
14 | end
15 | if data.writenum and data.writenum > 0 then
16 | return
17 | end
18 | end
19 |
20 | local record = cookie_map[data.cookie]
21 | if not record then
22 | return
23 | end
24 | cookie_map[data.cookie] = nil
25 | record.callback(data, record.callback_arg)
26 | end
27 |
28 | local function write_callback( data, ret, result_list )
29 | data["writenum"] = data["writenum"] - 1
30 | if ret ~= 0 then
31 | data.failed = true
32 | end
33 | check_finish(data)
34 | end
35 |
36 | local function accout_user_callback(data, ret, result_list)
37 | data["readnum"] = data["readnum"] - 1
38 | if type(result_list) ~= "table" or #result_list == 0 then
39 | data.failed = true
40 | else
41 | data["user"] = result_list[1]
42 | end
43 | check_finish(data)
44 | end
45 |
46 | local function item_callback(data, ret, result_list)
47 | data["readnum"] = data["readnum"] - 1
48 | if type(result_list) ~= "table" or ret ~= 0 then
49 | data.failed = true
50 | else
51 | data["item"] = result_list
52 | end
53 | check_finish(data)
54 | end
55 |
56 | local function equip_callback(data, ret, result_list)
57 | data["readnum"] = data["readnum"] - 1
58 | if type(result_list) ~= "table" or ret ~= 0 then
59 | data.failed = true
60 | else
61 | data["equip"] = result_list
62 | end
63 | check_finish(data)
64 | end
65 |
66 | local function ddz_info_callback(data, ret, result_list)
67 | data["readnum"] = data["readnum"] - 1
68 | if type(result_list) ~= "table" or ret ~= 0 then
69 | data.failed = true
70 | else
71 | data["ddz_info"] = result_list[1]
72 | end
73 | check_finish(data)
74 | end
75 |
76 |
77 | function load_data_from_db(rid, callback, callback_arg)
78 | ASSERT(callback ~= nil and type(callback) == "function", "callback must not empty")
79 |
80 | local table_list={
81 | {
82 | name = "user",
83 | condition = {_WHERE={rid=rid} },
84 | callback = accout_user_callback
85 | },
86 | {
87 | name = "item",
88 | condition = {_WHERE={owner=rid} },
89 | callback = item_callback
90 | },
91 | {
92 | name = "equip",
93 | condition = {_WHERE={owner=rid} },
94 | callback = equip_callback
95 | },
96 | {
97 | name = "ddz_info",
98 | condition = {_WHERE={owner=rid} },
99 | callback = ddz_info_callback
100 | }
101 | }
102 |
103 | local num = SIZEOF(table_list)
104 | local data = { rid = rid, cookie = new_cookie(), readnum = num, is_db = true }
105 | local record = {
106 | callback = callback,
107 | callback_arg = callback_arg,
108 | }
109 | cookie_map[data.cookie] = record
110 |
111 | for i=1, num do
112 | if table_list[i].name and table_list[i].condition and table_list[i].callback then
113 | local sql = SQL_D.select_sql(table_list[i].name, table_list[i].condition )
114 | DB_D.read_db(table_list[i].name , sql, table_list[i].callback, data)
115 | end
116 | end
117 |
118 | end
--------------------------------------------------------------------------------
/scripts/client/command.lua:
--------------------------------------------------------------------------------
1 |
2 | function connect(account, password)
3 | LOGIN_D.login(account, password)
4 | end
5 |
6 | function c()
7 | connect("aa", "bb")
8 | end
9 |
10 | function c1()
11 | connect("aa1", "bb1")
12 | end
13 |
14 | function c2()
15 | connect("aa2", "bb2")
16 | end
17 |
18 | local function command_send_message(...)
19 | local user = ME_D.get_user()
20 | if not IS_OBJECT(user) then
21 | TRACE("请先登陆游戏")
22 | return
23 | end
24 | user:send_message(...)
25 | end
26 |
27 | local function add_attrib(field, amount)
28 | command_send_message(CMD_COMMON_OP, {oper = "add", field = field, amount = amount})
29 | end
30 |
31 | local function cost_attrib(field, amount)
32 | command_send_message(CMD_COMMON_OP, {oper = "cost", field = field, amount = amount})
33 | end
34 |
35 | function add_gold(amount)
36 | add_attrib("gold", amount)
37 | end
38 |
39 | function cost_gold(amount)
40 | cost_attrib("gold", amount)
41 | end
42 |
43 | function add_stone(amount)
44 | add_attrib("stone", amount)
45 | end
46 |
47 | function cost_stone(amount)
48 | cost_attrib("stone", amount)
49 | end
50 |
51 | function add_exp(amount)
52 | add_attrib("exp", amount)
53 | end
54 |
55 | function add_item(class_id, amount)
56 | amount = amount or 1
57 | command_send_message(CMD_COMMON_OP, {oper = "add_item", class_id = class_id, amount = amount})
58 | end
59 |
60 | function show_items()
61 | local user = ME_D.get_user()
62 | if not IS_OBJECT(user) then
63 | TRACE("请先登陆游戏")
64 | return
65 | end
66 |
67 | local items = user:get_page_carry(PAGE_ITEM)
68 | table.sort(items, function(itema, itemb)
69 | local _, posa = READ_POS(itema:query("pos"))
70 | local _, posb = READ_POS(itemb:query("pos"))
71 | if posa == nil or posb == nil then
72 | return false
73 | end
74 | return posa < posb
75 | end)
76 | TRACE("您拥有的位置如下:")
77 | for _,item in ipairs(items) do
78 | TRACE("位置:%s, 物品rid:%s, 物品名称:%s, 物品数量:%d", item:query("pos"), item:query("rid"), item:query("name"), item:query("amount"))
79 | end
80 | end
81 |
82 | function show_equips()
83 | local user = ME_D.get_user()
84 | if not IS_OBJECT(user) then
85 | TRACE("请先登陆游戏")
86 | return
87 | end
88 | local equips = user:get_page_carry(PAGE_EQUIP)
89 | table.sort(equips, function(equipa, equipb)
90 | local _, posa = READ_POS(equipa:query("pos"))
91 | local _, posb = READ_POS(equipb:query("pos"))
92 | if posa == nil or posb == nil then
93 | return false
94 | end
95 | return posa < posb
96 | end)
97 | TRACE("您拥有的位置如下:")
98 | for _,equip in ipairs(equips) do
99 | TRACE("位置:%s, 物品rid:%s, 物品名称:%s, 物品数量:%d, 等级:%d", equip:query("pos"), equip:query("rid"), equip:query("name"), equip:query("amount"), equip:query("lv"))
100 | end
101 | end
102 |
103 | function sale_object(rid, amount)
104 | amount = amount or 1
105 | command_send_message(CMD_SALE_OBJECT, {rid = rid, amount = amount})
106 | end
107 |
108 | function send_chat(content)
109 | command_send_message(CMD_CHAT, CHAT_CHANNEL_WORLD, {send_content = content})
110 | end
111 |
112 | function enter_room(room_name)
113 | room_name = room_name or "ddz1"
114 | command_send_message(CMD_ENTER_ROOM, {room_name = room_name})
115 | end
116 |
117 | --桌号,进入方法(游戏(game),旁观(look))
118 | function enter_desk(idx, method)
119 | command_send_message(CMD_ROOM_MESSAGE, "enter_desk", {idx = idx, enter_method = method})
120 | end
121 |
122 | function leave_room()
123 | command_send_message(CMD_LEAVE_ROOM, {})
124 | end
125 |
126 | function desk_ready()
127 | command_send_message(CMD_ROOM_MESSAGE, "desk_op", {oper = "ready"})
128 | end
129 |
130 | function desk_choose(is_choose)
131 | command_send_message(CMD_ROOM_MESSAGE, "desk_op", {oper = "choose", is_choose = is_choose or 1})
132 | end
--------------------------------------------------------------------------------
/scripts/server/daemons/internal_commd.lua:
--------------------------------------------------------------------------------
1 | -- internal_commd.lua
2 | -- Created by wugd
3 | -- 发送信息内部管理的类
4 |
5 | -- 声明模块名
6 | INTERNAL_COMM_D = {}
7 | setmetatable(INTERNAL_COMM_D, {__index = _G})
8 | local _ENV = INTERNAL_COMM_D
9 |
10 | local cookie_map = {}
11 | local TIMEOUT = 15
12 |
13 | -- 超时处理函数
14 | local function timer_handle()
15 | local cur_time = os.time()
16 |
17 | for k, v in pairs(cookie_map) do
18 | if v["begin_time"] + TIMEOUT <= cur_time then
19 | cookie_map[k] = nil
20 | local callback, arg = v["callback"], v["arg"]
21 | if type(callback) == "function" then
22 | if arg then
23 | -- -2 表示超时
24 | callback(arg, -2)
25 | else
26 | callback(-2)
27 | end
28 | end
29 | end
30 | end
31 | end
32 |
33 | ---- 公共接口
34 |
35 | --收到其他服务器返回的消息
36 | function notify_internal_result(cookie, ...)
37 | local oper = cookie_map[tostring(cookie)]
38 | if not oper then
39 | do return end
40 | end
41 |
42 | --为正数,表明未超时
43 | local ret = 2
44 |
45 | -- 从 cookie_map 中移除该操作记录
46 | cookie_map[tostring(cookie)] = nil
47 |
48 | -- 取得该操作的回调函数
49 | local callback = oper["callback"]
50 | local callback_arg = oper["arg"]
51 |
52 | -- 若存在回调,则调用之,否则调用默认回调函数
53 | if type(callback) == "function" then
54 |
55 | -- 若有结果集
56 | if callback_arg then
57 | callback(callback_arg, ret, ...)
58 | else
59 | callback(ret, ...)
60 | end
61 | end
62 | end
63 |
64 | function send_room_raw_message(room_name, user_rid, record, net_data)
65 | --如果有回调函数,则产生一个cookie,默认cookie为该消息的第一个参数
66 | local cookie = 0
67 | if IS_TABLE(record) and SIZEOF(record) ~= 0 then
68 | cookie = new_cookie()
69 | local new_record = { begin_time = os.time(), callback = record[1], arg = record[2] }
70 | cookie_map[tostring(cookie)] = new_record
71 | end
72 |
73 | local channel = string.format(CREATE_ROOM_MSG_CHANNEL_USER, room_name, user_rid, cookie)
74 | REDIS_D.run_publish(channel, net_data)
75 | end
76 |
77 | -- 对指定的房间,指定的用户进行消息发送
78 | function send_room_message(room_name, user_rid, record, msg, ...)
79 | local net_msg = pack_message(get_common_msg_type(), msg, ...)
80 | if not net_msg then
81 | return
82 | end
83 |
84 | send_room_raw_message(room_name, user_rid, record, net_msg:get_data())
85 | del_message(net_msg)
86 | end
87 |
88 | function send_server_raw_message(server_id, user_rid, record, net_data)
89 | --如果有回调函数,则产生一个cookie,默认cookie为该消息的第一个参数
90 | local cookie = 0
91 | if IS_TABLE(record) and SIZEOF(record) ~= 0 then
92 | cookie = new_cookie()
93 | local new_record = { begin_time = os.time(), callback = record[1], arg = record[2] }
94 | cookie_map[tostring(cookie)] = new_record
95 | end
96 |
97 | local channel = string.format(CREATE_SERVER_MSG_USER, server_id, user_rid, cookie)
98 | REDIS_D.run_publish(channel, net_data)
99 | end
100 |
101 |
102 | -- 对指定的server_id进行消息发送
103 | function send_server_message(server_id, user_rid, record, msg, ...)
104 | server_id = tonumber(server_id)
105 | local net_msg = pack_message(get_common_msg_type(), msg, ...)
106 | if not net_msg then
107 | return
108 | end
109 |
110 | send_server_raw_message(server_id, user_rid, record, net_msg:get_data())
111 | del_message(net_msg)
112 | end
113 |
114 | function get_cookie_map()
115 | return cookie_map
116 | end
117 |
118 | -- 模块的入口执行
119 | function create()
120 | set_timer(1000, timer_handle, nil, true)
121 |
122 | -- if SERVER_TYPE == SERVER_LOGIC or STANDALONE then
123 | -- REDIS_D.add_subscribe_channel(string.format(REDIS_SERVER_MSG_USER, SERVER_ID))
124 | -- REDIS_D.add_subscribe_channel(string.format(REDIS_RESPONE_SERVER_INFO, SERVER_ID))
125 | -- end
126 |
127 | end
128 |
129 | create()
130 |
--------------------------------------------------------------------------------
/examples/server.rs:
--------------------------------------------------------------------------------
1 | extern crate mysql as my;
2 | extern crate log;
3 | extern crate env_logger;
4 | extern crate tunm;
5 |
6 | extern crate td_rthreadpool;
7 |
8 | extern crate commander;
9 | use commander::Commander;
10 | use std::thread;
11 | use log::{warn};
12 | use tunm::{GlobalConfig, LuaEngine, register_custom_func, MioEventMgr, FileUtils, DbPool, RedisPool, TelnetUtils, LogUtils};
13 |
14 | use std::env;
15 |
16 | use std::net::UdpSocket;
17 |
18 | /// get the local ip address, return an `Option`. when it fail, return `None`.
19 | pub fn get() -> Option {
20 | let socket = match UdpSocket::bind("0.0.0.0:0") {
21 | Ok(s) => s,
22 | Err(_) => return None,
23 | };
24 |
25 | match socket.connect("8.8.8.8:80") {
26 | Ok(()) => (),
27 | Err(_) => return None,
28 | };
29 |
30 | match socket.local_addr() {
31 | Ok(addr) => return Some(addr.ip().to_string()),
32 | Err(_) => return None,
33 | };
34 | }
35 |
36 | fn main() {
37 |
38 | let command = Commander::new()
39 | .version(&env!("CARGO_PKG_VERSION").to_string())
40 | .usage("test")
41 | .usage_desc("tunm server commander.")
42 | .option_str("-c, --config [value]", "config data ", Some("config/Gate.yaml".to_string()))
43 | .option_str("-s, --search [value]", "search data ", Some("scripts/".to_string()))
44 | .option_str("-l, --log [value]", "log4rs file config ", Some("config/log4rs.yml".to_string()))
45 | .parse_env_or_exit()
46 | ;
47 |
48 | log4rs::init_file(&*command.get_str("l").unwrap(), Default::default()).unwrap();
49 | warn!("local address!! {}", get().unwrap());
50 | let mut success = GlobalConfig::change_by_file(&*format!("local/{}", &*command.get_str("c").unwrap()));
51 | if !success {
52 | success = GlobalConfig::change_by_file(&*format!("config/{}", &*command.get_str("c").unwrap()));
53 | }
54 | if !success {
55 | success = GlobalConfig::change_by_file(&*command.get_str("c").unwrap());
56 | }
57 | if !success {
58 | panic!("加载配置文件失败");
59 | }
60 |
61 | let global_config = GlobalConfig::instance();
62 | assert_eq!(success, true);
63 |
64 | let success = DbPool::instance().set_db_info(global_config.db_info.clone());
65 | assert_eq!(success, true);
66 |
67 | let success = RedisPool::instance().set_url_list(global_config.get_redis_url_list());
68 | assert_eq!(success, true);
69 |
70 | let lua = LuaEngine::instance().get_lua();
71 | for (key, value) in &global_config.lua_macros {
72 | let value = &**value;
73 | if let Some(i) = value.trim().parse::().ok() {
74 | lua.set(&**key, i);
75 | } else if value.trim() == "true" {
76 | lua.set(&**key, true);
77 | } else if value.trim() == "false" {
78 | lua.set(&**key, false);
79 | } else if value.trim() == "false" {
80 | lua.set(&**key, false);
81 | } else {
82 | lua.set(&**key, value.trim_matches('"'));
83 | }
84 | }
85 |
86 | LogUtils::instance().set_log_path("log/".to_string());
87 |
88 | if let Some(path) = command.get_str("s") {
89 | FileUtils::instance().add_search_path(&*path);
90 | }
91 |
92 | // FileUtils::instance().add_search_path("scripts/");
93 | let telnet_addr = global_config.telnet_addr.clone().unwrap_or(String::new());
94 | if telnet_addr.len() > 2 {
95 | TelnetUtils::instance().listen(&*telnet_addr);
96 | }
97 |
98 | register_custom_func(lua);
99 | let _ : Option<()> = LuaEngine::instance().get_lua().exec_string(format!("require '{:?}'", global_config.start_lua));
100 | MioEventMgr::instance().add_lua_excute();
101 |
102 |
103 |
104 | thread::spawn(move || {
105 | let _ = MioEventMgr::instance().run_server();
106 | });
107 |
108 | let err = MioEventMgr::instance().run_timer();
109 |
110 | println!("Finish Server! {:?}", err);
111 | }
112 |
--------------------------------------------------------------------------------
/scripts/global/include/log_define.lua:
--------------------------------------------------------------------------------
1 | -- log_define.lua
2 | -- Created by wugd
3 | -- 定义模块名
4 |
5 | LOG_TYPE_LOGIN_FAIL = 1;
6 | LOG_TYPE_USER = 2;
7 | LOG_TYPE_TASK = 3;
8 | LOG_TYPE_EXP = 4;
9 | LOG_TYPE_GET_GOLD = 5;
10 | LOG_TYPE_COST_GOLD = 6;
11 | LOG_TYPE_GET_STONE = 7;
12 | LOG_TYPE_COST_STONE = 8;
13 | LOG_TYPE_GET_DUEL_COIN = 9;
14 | LOG_TYPE_COST_DUEL_COIN = 10;
15 | LOG_TYPE_GET_ARENA_COIN = 11;
16 | LOG_TYPE_COST_ARENA_COIN = 12;
17 | LOG_TYPE_GET_DRAGON_COIN = 13;
18 | LOG_TYPE_COST_DRAGON_COIN = 14;
19 | LOG_TYPE_GET_POINT = 15;
20 | LOG_TYPE_COST_POINT = 16;
21 | LOG_TYPE_LEVEL_UP = 18;
22 | LOG_TYPE_CREATE_PROPERTY = 19;
23 | LOG_TYPE_DESTRUCT_PROPERTY = 20;
24 | LOG_TYPE_LOGOUT = 21;
25 | LOG_TYPE_COST_AMOUNT = 25;
26 | LOG_TYPE_ADD_AMOUNT = 26;
27 | LOG_TYPE_LOGIN_RECORD = 39;
28 | LOG_TYPE_LOGOUT_RECORD = 40;
29 | LOG_TYPE_CREATE_NEW_USER = 41;
30 | LOG_TYPE_ENTER_MAP = 42;
31 | LOG_TYPE_WORLD_CHAT = 69;
32 |
33 |
34 | --日志产生的渠道\系统
35 | LOG_CHANNEL_NULL = 0
36 | LOG_CHANNEL_COPY = 1 --副本
37 | LOG_CHANNEL_OPERATION = 2 --运营活动
38 | LOG_CHANNEL_ARENA = 3 --竞技场
39 | LOG_CHANNEL_BAG = 4 --背包
40 | LOG_CHANNEL_HERO = 5 --英雄
41 | LOG_CHANNEL_TEAM = 6 --战队
42 |
43 |
44 | local _LOG_DESCRIBE_ = {
45 | [LOG_TYPE_LOGIN_FAIL] = {name = "玩家创建角色失败或则登录失败", p1="玩家账号或rid", p2="失败的原因1", p3="原因2", meno="" },
46 | [LOG_TYPE_EXP] = {name = "获得经验", p1="角色RID", p2="英雄RID或者角色RID", p3="经验值", meno="" },
47 | [LOG_TYPE_GET_GOLD] = {name = "获得金币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
48 | [LOG_TYPE_COST_GOLD] = {name = "消耗金币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
49 | [LOG_TYPE_GET_STONE] = {name = "获得钻石", p1="角色RID", p2="数量", p3="最终数量", meno="" },
50 | [LOG_TYPE_COST_STONE] = {name = "消耗钻石", p1="角色RID", p2="数量", p3="最终数量", meno="" },
51 | [LOG_TYPE_GET_DUEL_COIN] = {name = "获得决斗币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
52 | [LOG_TYPE_COST_DUEL_COIN] = {name = "消耗决斗币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
53 | [LOG_TYPE_GET_ARENA_COIN] = {name = "获得竞技币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
54 | [LOG_TYPE_COST_ARENA_COIN] = {name = "消耗竞技币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
55 | [LOG_TYPE_GET_DRAGON_COIN] = {name = "获得龙币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
56 | [LOG_TYPE_COST_DRAGON_COIN] = {name = "消耗龙币", p1="角色RID", p2="数量", p3="最终数量", meno="" },
57 | [LOG_TYPE_GET_POINT] = {name = "获得积分", p1="角色RID", p2="数量", p3="最终数量", meno="" },
58 | [LOG_TYPE_COST_POINT] = {name = "消耗积分", p1="角色RID", p2="数量", p3="最终数量", meno="" },
59 | [LOG_TYPE_LEVEL_UP] = {name = "玩家升级", p1="角色RID", p2="英雄RID或者角色RID", p3="之前等级->当前等级(1->2)", meno="" },
60 | [LOG_TYPE_CREATE_PROPERTY] = {name = "创建物件对象并加载到玩家身上", p1="属主rid", p2="物品rid", p3="物品class_id", meno="物品对象中的数量" },
61 | [LOG_TYPE_DESTRUCT_PROPERTY] = {name = "销毁物件对象并加载到玩家身上", p1="属主rid", p2="物品rid", p3="物品class_id", meno="物品对象中的数量" },
62 | [LOG_TYPE_COST_AMOUNT] = {name = "删除道具", p1="属主rid", p2="物品rid", p3="物品class_id", meno="删除个数,删除后剩余个数(cost:%d|remain:%d)" },
63 | [LOG_TYPE_ADD_AMOUNT] = {name = "新增道具", p1="属主rid", p2="物品rid", p3="物品class_id", meno="新增个数,新增后剩余个数(add:%d|remain:%d)" },
64 | [LOG_TYPE_LOGOUT] = {name = "调用玩家登出操作", p1="玩家rid", p2="port_no的值", p3="调用登出接口的函数名", meno="" },
65 | [LOG_TYPE_LOGIN_RECORD] = {name = "玩家登录记录", p1="玩家RID", p2="玩家ACCOUNT", p3="", meno="" },
66 | [LOG_TYPE_LOGOUT_RECORD] = {name = "玩家登出记录", p1="玩家RID", p2="玩家ACCOUNT", p3="", meno="" },
67 | [LOG_TYPE_CREATE_NEW_USER] = {name = "进入副本", p1="玩家RID", p2="玩家ACCOUNT", p3="", meno="" },
68 | [LOG_TYPE_ENTER_MAP] = {name = "新角色创建记录", p1="玩家RID", p2="英雄RID和副手RID(%s:%s)", p3="副本rno", meno="" },
69 | [LOG_TYPE_WORLD_CHAT] = {name = "世界聊天", p1="玩家RID", p2="发送内容长度", p3="", meno="" },
70 | }
71 |
--------------------------------------------------------------------------------
/scripts/data/txt/appearance.txt:
--------------------------------------------------------------------------------
1 | #该表存储各个实体要下发给客户端的信息的字段名;在各个状态下,判断某个字段是否要发送
2 | string string string string
3 | type attrib group desc
4 | OB_TYPE_USER rid * RID
5 | OB_TYPE_USER name * 名字
6 | OB_TYPE_USER level SELF|SIMPLE|DETAIL 玩家等级
7 | OB_TYPE_USER 1-* SELF 装扮页面
8 | OB_TYPE_USER 2-* SELF 装扮页面
9 | OB_TYPE_USER 3-* SELF 装扮页面
10 | OB_TYPE_USER 4-* SELF 装扮页面
11 | OB_TYPE_USER 5-* SELF 装扮页面
12 | OB_TYPE_USER 7-* SELF 装扮页面
13 | OB_TYPE_USER 8-* SELF|CARRY 装扮页面
14 | OB_TYPE_USER 9-* SELF|CARRY 装扮页面
15 | OB_TYPE_USER 10-* SELF 装扮页面
16 | OB_TYPE_USER 14-* SELF 宝石背包
17 | OB_TYPE_USER 15-* SELF 考古背包
18 | OB_TYPE_USER 16-* SELF 已装备宝石页面
19 | OB_TYPE_USER 17-* SELF 已装备的印鉴页面
20 | OB_TYPE_USER direction SIMPLE 朝向
21 | OB_TYPE_USER money SELF 金钱
22 | OB_TYPE_USER flower SELF 鲜花数
23 | OB_TYPE_USER account SELF|DETAIL 帐号
24 | OB_TYPE_USER online_time SELF 当天在线时间(秒)
25 | OB_TYPE_USER status SELF|SIMPLE 玩家状态
26 | OB_TYPE_USER status_cond SELF 状态条件
27 | OB_TYPE_USER shrine_count SELF 玩家精灵神殿挑战次数
28 | OB_TYPE_USER shrine_pos SELF 玩家精灵神殿挑战位置
29 | OB_TYPE_USER flower_send_times SELF 玩家通过各种方式赠送鲜花的次数
30 | OB_TYPE_USER item_store_size SELF 道具仓库大小
31 | OB_TYPE_USER exp SELF 阅历值
32 | OB_TYPE_USER pet_exp SELF 魂槽
33 | OB_TYPE_USER skill SELF 召唤师技能
34 | OB_TYPE_USER improvement SELF 附加属性
35 | OB_TYPE_USER gender SELF|SIMPLE|DETAIL 玩家性别
36 | OB_TYPE_USER his_evaluate SELF 副本历史评价
37 | OB_TYPE_USER cur_container_size SELF 当前背包大小
38 | OB_TYPE_USER head SELF|DETAIL 头像
39 | OB_TYPE_USER server_id DETAIL 玩家服务器编号
40 | OB_TYPE_USER embattle_list SELF 玩家当前布阵列表
41 | OB_TYPE_USER soul SELF 灵魂碎片
42 | OB_TYPE_USER warrior_tower_info SELF 勇士之塔信息
43 | OB_TYPE_USER vp SELF 玩家疲劳值
44 | OB_TYPE_USER trousers SELF|SIMPLE|DETAIL 裤子
45 | OB_TYPE_USER frock SELF|SIMPLE|DETAIL 衣服
46 | OB_TYPE_USER right SELF|SIMPLE|DETAIL 武器
47 | OB_TYPE_USER shoes SELF|SIMPLE|DETAIL 鞋子
48 | OB_TYPE_USER hair SELF|SIMPLE|DETAIL 头盔
49 | OB_TYPE_USER week_money SELF 每周金钱
50 | OB_TYPE_USER week_level SELF 每周提升等级
51 | OB_TYPE_USER week_soul SELF 每周灵魂碎片
52 | OB_TYPE_USER week_flower SELF 每周鲜花获得
53 | OB_TYPE_USER assign_daily_times SELF 日悬赏任务已接受次数
54 | OB_TYPE_USER activity SELF 活动数据
55 | OB_TYPE_USER daily_refresh_time SELF 日悬赏任务刷新时间
56 | OB_TYPE_USER shortcut_key SELF 玩家快捷键
57 | OB_TYPE_USER enter_train_times SELF 每日进入试炼之地的次数
58 | OB_TYPE_USER deblock_level SELF 玩家已解封到的等级
59 | OB_TYPE_USER user_title SELF|SIMPLE 玩家正在使用的称号
60 | OB_TYPE_USER incubate SELF 机甲龙孵化系统数据
61 | OB_TYPE_USER interact_time SELF 机甲龙孵化系统时间数据
62 | OB_TYPE_USER family_name SIMPLE 在场景外观中显示家族名字
63 | OB_TYPE_USER family_daily_value SELF 家族领取奖励的判断数据
64 | OB_TYPE_ITEM rid * RID
65 | OB_TYPE_ITEM class_id SELF|SIMPLE CLASS_ID
66 | OB_TYPE_ITEM pos SELF 位置
67 | OB_TYPE_ITEM amount SELF|SIMPLE 数量
68 | OB_TYPE_ITEM ob_type SELF|SIMPLE 对象类型
69 | OB_TYPE_ITEM owner_rid SIMPLE 所有者
70 | OB_TYPE_ITEM disappear SELF 第二类限时道具消失时间
71 | OB_TYPE_NPC rid * RID
72 | OB_TYPE_NPC name SELF|SIMPLE 名字
73 | OB_TYPE_NPC class_id SELF|SIMPLE CLASS_ID
74 | OB_TYPE_NPC npc_id SELF|SIMPLE NPC编号
75 | OB_TYPE_NPC direction SIMPLE 朝向
76 | OB_TYPE_MONSTER rid * RID
77 | OB_TYPE_MONSTER name SELF|SIMPLE 名字
78 | OB_TYPE_MONSTER max_hp SELF|SIMPLE 最大血量值
79 | OB_TYPE_MONSTER hp SELF|SIMPLE 当前血量值
80 | OB_TYPE_MONSTER level SELF|SIMPLE 宠物等级
81 | OB_TYPE_MONSTER class_id SELF|SIMPLE CLASS_ID
82 | OB_TYPE_MONSTER direction SIMPLE 朝向
83 | OB_TYPE_MONSTER ob_type SELF|SIMPLE 对象类型
84 | OB_TYPE_MONSTER boss_type SIMPLE 是否为BOSS
85 | OB_TYPE_MONSTER skill SIMPLE 技能
86 | OB_TYPE_MONSTER speed SELF|SIMPLE 移动速度
87 | OB_TYPE_HERO rid * RID
88 | OB_TYPE_HERO horoscope SELF|SIMPLE 星座
89 | OB_TYPE_HERO star SELF|SIMPLE 星级
90 | OB_TYPE_HERO exp SELF 经验
91 | OB_TYPE_HERO leader SELF 站位
92 | OB_TYPE_HERO lv SELF|SIMPLE 英雄 等级
93 | OB_TYPE_HERO class_id SELF|SIMPLE CLASS_ID
94 | OB_TYPE_HERO skills SELF|SIMPLE 技能
95 | OB_TYPE_HERO owner SIMPLE 属主
96 | OB_TYPE_HERO ob_type SELF|SIMPLE 对象类型
97 | OB_TYPE_EQUIP rid *|CARRY RID
98 | OB_TYPE_EQUIP class_id SELF|SIMPLE|CARRY CLASS_ID
99 | OB_TYPE_EQUIP pos SELF|CARRY 位置
100 | OB_TYPE_EQUIP ob_type SELF|SIMPLE|CARRY 对象类型
101 | OB_TYPE_EQUIP lv SELF|CARRY 强化等级
102 | OB_TYPE_EQUIP owner SIMPLE|CARRY 所有者
103 | OB_TYPE_EQUIP exp SELF|CARRY 装备经验(宝石)
104 |
--------------------------------------------------------------------------------
/scripts/server/clone/rooms/desk.lua:
--------------------------------------------------------------------------------
1 | --room.lua
2 | --Created by wugd
3 | --桌子类
4 |
5 | --创建类模板
6 | DESK_TDCLS = tdcls()
7 | DESK_TDCLS.name = "DESK_TDCLS"
8 |
9 | --构造函数
10 | function DESK_TDCLS:create(room, idx)
11 | self.room = room
12 | self.idx = idx
13 | --rid=wheel, 玩家所在的位置
14 | self.users = {}
15 | --玩家所坐的位置
16 | self.wheels = {{}, {}, {}}
17 | end
18 |
19 | function DESK_TDCLS:time_update()
20 |
21 | end
22 |
23 | function DESK_TDCLS:is_full_user()
24 | return true
25 | end
26 |
27 | function DESK_TDCLS:get_user_count()
28 | return SIZEOF(self.users)
29 | end
30 |
31 | function DESK_TDCLS:get_empty_wheel()
32 | for idx,v in ipairs(self.wheels) do
33 | if not is_rid_vaild(v.rid) then
34 | return idx
35 | end
36 | end
37 | return nil
38 | end
39 |
40 |
41 | function DESK_TDCLS:check_user_wheel(user_rid)
42 | local fix_empty_idx = nil
43 | for idx,v in ipairs(self.wheels) do
44 | if v.rid == user_rid then
45 | return idx
46 | end
47 | if not is_rid_vaild(v.rid) and not fix_empty_idx then
48 | fix_empty_idx = idx
49 | end
50 | end
51 | if self:is_playing() then
52 | return nil
53 | end
54 | return fix_empty_idx
55 | end
56 |
57 | function DESK_TDCLS:is_empty()
58 | for idx,v in ipairs(self.wheels) do
59 | if is_rid_vaild(v.rid) then
60 | return false
61 | end
62 | end
63 | return true
64 | end
65 |
66 | function DESK_TDCLS:user_enter(user_rid)
67 | local idx = self:check_user_wheel(user_rid)
68 | if not idx then
69 | return -1
70 | end
71 | self.users[user_rid] = { idx = idx }
72 | self.wheels[idx] = self.wheels[idx] or {}
73 | self.wheels[idx].rid = user_rid
74 | self.wheels[idx].is_ready = 0
75 |
76 | self:broadcast_message(MSG_ROOM_MESSAGE, "success_enter_desk", {rid = user_rid, wheel_idx = idx, idx = self.idx, info = self.room:get_base_info_by_rid(user_rid)})
77 | self:send_desk_info(user_rid)
78 | return 0
79 | end
80 |
81 | function DESK_TDCLS:send_desk_info(user_rid)
82 |
83 | end
84 |
85 | function DESK_TDCLS:user_leave(user_rid)
86 | local user_data = self.users[user_rid]
87 | if not user_data then
88 | return -1
89 | end
90 | user_data.last_logout_time = os.time()
91 |
92 | self:broadcast_message(MSG_ROOM_MESSAGE, "success_leave_desk", {rid = user_rid, wheel_idx = user_data.idx})
93 | --中途掉线,保存当前进度数据
94 | if self:is_playing() then
95 | return -1
96 | end
97 | self.users[user_rid] = nil
98 | self.wheels[user_data.idx] = {}
99 | return 0
100 | end
101 |
102 |
103 | -- 广播消息
104 | function DESK_TDCLS:broadcast_message(msg, ...)
105 |
106 | local size = SIZEOF(self.users)
107 | local msg_buf = pack_message(get_common_msg_type(), msg, ...)
108 |
109 | if not msg_buf then
110 | TRACE("广播消息(%d)打包消息失败。", msg)
111 | return
112 | end
113 |
114 | -- 遍历该房间的所有玩家对象
115 | for rid, _ in pairs(self.users) do
116 | self.room:send_rid_raw_message(rid, {}, msg_buf)
117 | end
118 |
119 | del_message(msg_buf)
120 | end
121 |
122 | -- 广播消息
123 | function DESK_TDCLS:send_rid_message(user_rid, msg, ...)
124 | local msg_buf = pack_message(get_common_msg_type(), msg, ...)
125 | if not msg_buf then
126 | TRACE("发送消息(%s:%o)打包消息失败。", msg, {...})
127 | return
128 | end
129 |
130 | self.room:send_rid_raw_message(user_rid, {}, msg_buf)
131 |
132 | del_message(msg_buf)
133 | end
134 |
135 | function DESK_TDCLS:op_info(user_rid, info)
136 | return false
137 | end
138 |
139 | function DESK_TDCLS:check_all_ready()
140 | for _,data in ipairs(self.wheels) do
141 | if data.is_ready ~= 1 then
142 | return false
143 | end
144 | end
145 |
146 | self:start_game()
147 | end
148 |
149 | function DESK_TDCLS:start_game()
150 | self:broadcast_message(MSG_ROOM_MESSAGE, "success_start_game", {idx = self.idx})
151 | end
152 |
153 | function DESK_TDCLS:is_playing(user_rid)
154 | return false
155 | end
156 |
157 | function DESK_TDCLS:get_play_num()
158 | return 3
159 | end
160 |
--------------------------------------------------------------------------------
/scripts/server/daemons/attribd.lua:
--------------------------------------------------------------------------------
1 | -- attribd.lua
2 | -- 负责属性相关的功能模块
3 |
4 | -- 声明模块名
5 | ATTRIB_D = {}
6 | setmetatable(ATTRIB_D, {__index = _G})
7 | local _ENV = ATTRIB_D
8 |
9 | attrib_formula = {}
10 | attrib_max_list = {}
11 |
12 | ---- 定义公共接口,按照字母顺序排序
13 |
14 | function get_max_attrib(ob_type, key)
15 | local attrib_max = attrib_max_list[ob_type] or {}
16 | return attrib_max[key]
17 | end
18 |
19 | -- 查询对象属性值
20 | function query_attrib(ob, attrib)
21 | local ob_type = ob:query("ob_type")
22 | local attrib_info = attrib_formula[ob_type]
23 | if not attrib_info then
24 | -- 该属性没有配置相应的公式
25 | return (ob:query(attrib))
26 | end
27 |
28 | local formula = attrib_info[attrib]
29 | if not formula then
30 | -- 该对象的该属性没有配置相应的公式
31 | return (ob:query(attrib))
32 | end
33 |
34 | -- 调用公式取得该属性值
35 | return (formula(ob, attrib))
36 | end
37 |
38 | --执行属性增加操作
39 | function add_attrib(ob, field, value)
40 | if not IS_OBJECT(ob) or value == 0 then
41 | return false
42 | end
43 |
44 | --增加金钱,将ob变成玩家ob
45 | if field == "money" then
46 | if not ob or ob:query("ob_type") ~= OB_TYPE_USER then
47 | return false
48 | end
49 | end
50 |
51 | local value = math.floor(value)
52 | local cur_value = ob:query(field)
53 |
54 | --不存在字段field,则初始化为0
55 | if not cur_value then
56 | cur_value = 0
57 | end
58 |
59 | local final_value = value + math.floor(cur_value)
60 | local attrib_max = attrib_max_list[ob:query("ob_type")] or {}
61 |
62 | --查看是否达到满级
63 | if field == "exp" and not attrib_max["lv"] then
64 | if attrib_max["lv"] <= ob:query("lv") then
65 | return false
66 | end
67 | end
68 |
69 | --超过上限,则取上限值
70 | if attrib_max[field] and final_value > attrib_max[field] then
71 | final_value = attrib_max[field]
72 | end
73 |
74 | ob:set(field, final_value)
75 | ob:notify_fields_updated(field)
76 |
77 | if field == "exp" then
78 | raise_issue(EVENT_EXP_CHANGE, ob)
79 | end
80 | return true
81 | end
82 |
83 | --执行属性消耗操作
84 | function cost_attrib(ob, field, value)
85 | if not IS_OBJECT(ob) then
86 | return false
87 | end
88 |
89 | local value = math.ceil(value)
90 | local cur_value = ob:query(field)
91 |
92 | --不存在字段field,或者结果小于零, 扣除失败
93 | if not cur_value or cur_value - value < 0 or value == 0 then
94 | return false
95 | end
96 |
97 | local final_value = cur_value - value
98 |
99 | ob:set(field, final_value)
100 | ob:notify_fields_updated(field)
101 |
102 | if field == "gold" then
103 | -- 发起消耗金币的事件
104 | raise_issue(EVENT_GOLD_COST, ob, -1*value)
105 | elseif field == "stone" then
106 | -- 发起消耗钻石事件
107 | raise_issue(EVENT_STONE_COST, ob, -1*value)
108 | elseif field == "sp" then
109 | -- 发起消耗钻石事件
110 | raise_issue(EVENT_PHY_COST, ob, -1*value)
111 | end
112 |
113 | -- if statistics_cost_log_id[field] then
114 | -- local owner_ob = get_owner(ob)
115 | -- if owner_ob then
116 | -- LOG_D.to_log(statistics_cost_log_id[field], get_ob_rid(owner_ob), tostring(value), tostring(final_value), "", owner_ob:query_log_channel())
117 | -- end
118 | -- end
119 |
120 | return true
121 | end
122 |
123 |
124 | -- 模块的入口执行
125 | function create()
126 | local data = IMPORT_D.readcsv_to_tables("data/txt/attrib_formula.txt")
127 |
128 | local attrib, ob_type
129 | for _, info in ipairs(data) do
130 | attrib = info["attrib"]
131 | info["ob_type"] = _G[info["ob_type"]]
132 | ob_type = info["ob_type"]
133 |
134 | if not IS_MAPPING(attrib_formula[ob_type]) then
135 | attrib_formula[ob_type] = {}
136 | end
137 |
138 | attrib_formula[ob_type][attrib] = _G[info["formula"]]
139 | end
140 |
141 | local max_list = IMPORT_D.readcsv_to_tables("data/txt/attrib_max.txt")
142 | for _, info in pairs(max_list) do
143 | info["ob_type"] = _G[info["ob_type"]]
144 | attrib_max_list[info.ob_type] = attrib_max_list[info.ob_type] or {}
145 | attrib_max_list[info.ob_type][info.name] = info.max
146 | end
147 |
148 | end
149 |
150 | create()
151 |
--------------------------------------------------------------------------------
/scripts/server/daemons/redis_queued.lua:
--------------------------------------------------------------------------------
1 | --redis_queued.lua
2 | --redis消息队列处理
3 | REDIS_QUEUED = {}
4 | setmetatable(REDIS_QUEUED, {__index = _G})
5 | local _ENV = REDIS_QUEUED
6 |
7 | function deal_with_reply(reply)
8 | if not IS_TABLE(reply) then
9 | return
10 | end
11 |
12 | -- TRACE("__ REDIS_QUEUED:deal_with_reply() __ is %o ", reply)
13 | if reply.channel == REDIS_CHAT_CHANNEL_WORLD then
14 | CHAT_D.deal_with_new_chat(DECODE_JSON(reply.payload))
15 | elseif reply.channel == SUBSCRIBE_ROOM_DETAIL_RECEIVE then
16 | ROOM_D.redis_room_detail(DECODE_JSON(reply.payload))
17 | elseif reply.channel == REDIS_ACCOUNT_START_HIBERNATE then
18 | if not is_rid_vaild(reply.payload) then
19 | return
20 | end
21 | raise_issue(EVENT_ACCOUNT_START_HIBERNATE, reply.payload)
22 | elseif reply.channel == REDIS_ACCOUNT_END_HIBERNATE then
23 | if not is_rid_vaild(reply.payload) then
24 | return
25 | end
26 | raise_issue(EVENT_ACCOUNT_END_HIBERNATE, reply.payload)
27 | elseif reply.channel == REDIS_ACCOUNT_OBJECT_CONSTRUCT then
28 | if not is_rid_vaild(reply.payload) then
29 | return
30 | end
31 | raise_issue(EVENT_ACCOUNT_OBJECT_CONSTRUCT, reply.payload)
32 | elseif reply.channel == REDIS_ACCOUNT_OBJECT_DESTRUCT then
33 | if not is_rid_vaild(reply.payload) then
34 | return
35 | end
36 | raise_issue(EVENT_ACCOUNT_OBJECT_DESTRUCT, reply.payload)
37 | elseif reply.channel == REDIS_NOTIFY_ACCOUNT_OBJECT_DESTRUCT then
38 | if not is_rid_vaild(reply.payload) then
39 | return
40 | end
41 | raise_issue(EVENT_NOTIFY_ACCOUNT_OBJECT_DESTRUCT, reply.payload)
42 | elseif reply.channel == REDIS_ACCOUNT_WAIT_LOGIN then
43 | if not is_rid_vaild(reply.payload) then
44 | return
45 | end
46 | raise_issue(EVENT_ACCOUNT_WAIT_LOGIN, reply.payload)
47 | elseif reply.channel == REDIS_USER_ENTER_WORLD then
48 | local data = DECODE_JSON(reply.payload)
49 | if not is_rid_vaild(data.rid) then
50 | return
51 | end
52 | raise_issue(EVENT_USER_OBJECT_CONSTRUCT, data.rid, data.server_id)
53 | elseif reply.channel == REDIS_USER_CONNECTION_LOST then
54 | if not is_rid_vaild(reply.payload) then
55 | return
56 | end
57 | raise_issue(EVENT_USER_CONNECTION_LOST, reply.payload)
58 |
59 | else
60 | local room_name, user_rid, cookie = string.match(reply.channel, MATCH_ROOM_MSG_CHANNEL_USER)
61 | if room_name and user_rid then
62 | ROOM_D.redis_dispatch_message(room_name, user_rid, cookie, reply.payload)
63 | return
64 | end
65 |
66 | local server_id, user_rid, cookie = string.match(reply.channel, MATCH_SERVER_MSG_USER)
67 | if server_id and user_rid then
68 | local user = find_object_by_rid(user_rid)
69 | if not user then
70 | return
71 | end
72 | local name, net_msg = pack_raw_message(reply.payload)
73 | if not net_msg then
74 | return
75 | end
76 | if get_message_type(name) == MESSAGE_LOGIC then
77 | oper_message(user, name, net_msg)
78 | else
79 | user:send_net_msg(net_msg)
80 | end
81 | del_message(net_msg)
82 | return
83 | end
84 |
85 |
86 | local server_id, cookie = string.match(reply.channel, MATCH_RESPONE_SERVER_INFO)
87 | if server_id and cookie then
88 | INTERNAL_COMM_D.notify_internal_result(cookie, reply.payload)
89 | return
90 | end
91 |
92 | local room_name, cookie = string.match(reply.channel, MATCH_ROOM_MSG_CHANNEL_USER)
93 | if room_name and cookie then
94 |
95 | return
96 | end
97 | end
98 | end
99 |
100 | function deal_with_respone_list(respone_list)
101 | for _,reply in ipairs(respone_list) do
102 | deal_with_reply(reply)
103 | end
104 | end
105 |
106 | local function time_update()
107 | local respone_list = REDIS_D.subs_get_reply()
108 | if respone_list ~= nil and #respone_list > 0 then
109 | deal_with_respone_list(respone_list)
110 | end
111 | end
112 |
113 | function create()
114 | set_timer(100, time_update, nil, true)
115 | end
116 |
117 | create()
--------------------------------------------------------------------------------
/scripts/global/clone/item.lua:
--------------------------------------------------------------------------------
1 | -- item.lua
2 | -- Created by wugd
3 | -- 道具对象类
4 |
5 | -- 创建类模板
6 | ITEM_TDCLS = tdcls(DBASE_TDCLS, RID_TDCLS, PROPERTY_TDCLS)
7 | ITEM_TDCLS.name = "ITEM_TDCLS"
8 |
9 | -- 构造函数
10 | function ITEM_TDCLS:create(value)
11 | ASSERT(type(value) == "table", "item::create para not corret")
12 | ASSERT(IS_INT(value["class_id"]))
13 |
14 | self:replace_dbase(value)
15 |
16 | if not value["amount"] then
17 | -- 设置数量为1
18 | self:set("amount", 1)
19 | else
20 | local max_count = CALC_ITEM_MAX_AMOUNT(self)
21 | if value["amount"] > max_count then
22 | TRACE("创建道具(%s/%d)的数量(%d)超过最大可叠加数(%d),请检查。",
23 | self:query("rid"), self:query("class_id"), value["amount"], max_count)
24 | end
25 | end
26 | end
27 |
28 | -- 析构函数
29 | function ITEM_TDCLS:destruct()
30 | end
31 |
32 | -- 生成对象的唯一ID
33 | function ITEM_TDCLS:get_ob_id()
34 | return (string.format("ITEM_TDCLS:%s:%s", SAVE_STRING(self:query("rid")),
35 | SAVE_STRING(self:query("class_id"))))
36 | end
37 |
38 | -- 定义公共接口,按照字母顺序排序
39 |
40 | --获取基本类的对象
41 | function ITEM_TDCLS:basic_object()
42 | local class_id = self.dbase["class_id"]
43 | return (find_basic_object_by_class_id(class_id))
44 | end
45 |
46 | -- 道具增加数量
47 | function ITEM_TDCLS:add_amount(count)
48 | if count <= 0 then
49 | return
50 | end
51 |
52 | local amount = self:query("amount")
53 | amount = amount + count
54 |
55 | if amount > CALC_ITEM_MAX_AMOUNT(self) then
56 | TRACE("增加道具数量(%d)超过该物品的最大可叠加数。", amount)
57 | return
58 | end
59 |
60 | -- 更新 amount 字段
61 | self:set("amount", amount)
62 | self:notify_fields_updated({"amount"})
63 |
64 | local memo = string.format("add:%d|remain:%d", count, self:query("amount"))
65 | LOG_D.to_log(LOG_TYPE_ADD_AMOUNT, self:query("owner"), self:GET_RID(),
66 | tostring(self:query("class_id")), memo, find_object_by_rid(self:query("owner")):query_log_channel())
67 | end
68 |
69 | -- 道具是否可叠加
70 | function ITEM_TDCLS:can_combine(ob)
71 | if not self:query("over_lap") or
72 | not ob:query("over_lap") or
73 | self:query("over_lap") <= 1 or
74 | ob:query("over_lap") <= 1 then
75 | return false
76 | end
77 |
78 | -- 判断道具是否为同一个道具
79 | if self == ob or
80 | self:query("class_id") ~= ob:query("class_id") then
81 | return false
82 | end
83 |
84 | -- 判断道具叠加后总数是否超过最大叠加数
85 | if self:query("amount") + ob:query("amount") > CALC_ITEM_MAX_AMOUNT(ob) then
86 | return false
87 | end
88 |
89 | return true
90 | end
91 |
92 | -- 道具扣除数量,返回实际扣除个数
93 | function ITEM_TDCLS:cost_amount(count)
94 | local owner = get_owner(self)
95 | local amount = self:query("amount")
96 | local update_amount = amount - count
97 |
98 | if update_amount <= 0 then
99 | owner:get_container():drop(self)
100 | return 0
101 | end
102 |
103 | -- 更新 amount 字段
104 | self:set("amount", update_amount)
105 | self:notify_fields_updated({"amount"})
106 |
107 | local memo = string.format("cost:%d|remain:%d", count, self:query("amount"))
108 | LOG_D.to_log(LOG_TYPE_COST_AMOUNT, self:query("owner"), self:GET_RID(),
109 | tostring(self:query("class_id")), memo, owner:query_log_channel() )
110 | return count
111 | end
112 |
113 | -- 通知字段变更
114 | function ITEM_TDCLS:notify_fields_updated(field_names)
115 | local owner = get_owner(self)
116 | if not owner then
117 | return
118 | end
119 |
120 | owner:notify_property_updated(get_ob_rid(self), field_names)
121 | end
122 |
123 | function ITEM_TDCLS:is_item()
124 | return true
125 | end
126 |
127 | -- 取得数据库的保存操作
128 | function ITEM_TDCLS:get_save_oper()
129 | local oper = self:query_temp("not_in_db") and "insert" or "update"
130 | return "item", self:query("rid"), oper
131 | end
132 |
133 | -- 取得保存数据库的信息
134 | function ITEM_TDCLS:save_to_mapping()
135 | --insert操作,返回全部数据
136 | if self:quermy_temp("not_in_db") then
137 | return (self:query())
138 | end
139 |
140 | -- 道具数据发生变化的字段
141 | local change_list = self:get_change_list()
142 | local data = {}
143 |
144 | for key,_ in pairs(change_list) do
145 | if DATA_D.is_field_exist("item", key) then
146 | data[key] = self:query(key)
147 | else
148 | return (self:query())
149 | end
150 | end
151 |
152 | if SIZEOF(data) == 0 then
153 | return
154 | end
155 |
156 | return data, 1
157 | end
158 |
--------------------------------------------------------------------------------
/scripts/global/include/define.lua:
--------------------------------------------------------------------------------
1 | -- define.lua
2 | -- Created by wugd
3 | -- 定义全局变量
4 |
5 |
6 | MSG_TYPE_TD = 0;
7 | MSG_TYPE_JSON = 1;
8 | MSG_TYPE_BIN = 2;
9 | MSG_TYPE_TEXT = 3;
10 |
11 | -- 连接类型
12 | CONN_TYPE_CLIENT = 1;
13 | CONN_TYPE_GS = 2;
14 | CONN_TYPE_YUNYING = 3;
15 |
16 | SERVER_GATE = "gate"
17 | SERVER_LOGIC = "logic"
18 | SERVER_CLIENT = "client"
19 |
20 | MESSAGE_GATE = "gate"
21 | MESSAGE_LOGIC = "logic"
22 | MESSAGE_SERVER = "server"
23 | MESSAGE_CLIENT = "client"
24 |
25 | GLOABL_RID = "000000000000"
26 | SYSTEM_RID = "111111111111"
27 |
28 | MESSAGE_MANAGE = 1
29 | MESSAGE_FORWARD = 2
30 | MESSAGE_DISCARD = 3
31 |
32 | REDIS_CHAT_CHANNEL_WORLD = "tunm:SUBSCRIBE:CHAT:CHANNEL:WORLD"
33 | REDIS_USER_CONNECTION_LOST = "tunm:SUBSCRIBE:REDIS:USER:CONNECTION:LOST"
34 | REDIS_NOTIFY_ACCOUNT_OBJECT_DESTRUCT = "tunm:SUBSCRIBE:NOTIFY:REDIS:ACCOUNT:OBJECT:DESTRUCT"
35 | REDIS_ACCOUNT_WAIT_LOGIN = "tunm:SUBSCRIBE:REDIS:ACCOUNT:WAIT:LOGIN"
36 | REDIS_ACCOUNT_CANCEL_WAIT_LOGIN = "tunm:SUBSCRIBE:REDIS:ACCOUNT:CANCEL:WAIT:LOGIN"
37 | REDIS_ACCOUNT_OBJECT_CONSTRUCT = "tunm:SUBSCRIBE:REDIS:ACCOUNT:OBJECT:CONSTRUCT"
38 | REDIS_ACCOUNT_OBJECT_DESTRUCT = "tunm:SUBSCRIBE:REDIS:ACCOUNT:OBJECT:DESTRUCT"
39 | REDIS_ACCOUNT_START_HIBERNATE = "tunm:SUBSCRIBE:ACCOUNT:START:HIBERNATE"
40 | REDIS_ACCOUNT_END_HIBERNATE = "tunm:SUBSCRIBE:ACCOUNT:END:HIBERNATE"
41 | REDIS_USER_ENTER_WORLD = "tunm:SUBSCRIBE:REDIS:USER:ENTER:WORLD"
42 |
43 | REDIS_SUBS_REGISTER =
44 | {
45 | REDIS_CHAT_CHANNEL_WORLD,
46 | REDIS_USER_CONNECTION_LOST,
47 | REDIS_NOTIFY_ACCOUNT_OBJECT_DESTRUCT,
48 | REDIS_ACCOUNT_WAIT_LOGIN,
49 | REDIS_ACCOUNT_CANCEL_WAIT_LOGIN,
50 | REDIS_ACCOUNT_OBJECT_CONSTRUCT,
51 | REDIS_ACCOUNT_OBJECT_DESTRUCT,
52 | REDIS_USER_ENTER_WORLD,
53 | }
54 |
55 | --server_id, user_rid, cookie
56 | REDIS_SERVER_MSG_USER = "SUBSCRIBE:SERVER:MSG:%d:*:*"
57 | MATCH_SERVER_MSG_USER = "SUBSCRIBE:SERVER:MSG:(%d+):(%w+):(%d+)"
58 | CREATE_SERVER_MSG_USER = "SUBSCRIBE:SERVER:MSG:%d:%s:%d"
59 |
60 | --server_id, cookie
61 | REDIS_RESPONE_SERVER_INFO = "SUBSCRIBE:RESPONE:SERVER:INFO:%d:*"
62 | MATCH_RESPONE_SERVER_INFO = "SUBSCRIBE:RESPONE:SERVER:INFO:(%d+):(%d+)"
63 | CREATE_RESPONE_SERVER_INFO = "SUBSCRIBE:RESPONE:SERVER:INFO:%d:%d"
64 |
65 | --room, user_rid, cookie
66 | REDIS_ROOM_MSG_CHANNEL_USER = "SUBSCRIBE:ROOM:MSG:CHANNEL:%s:*:*"
67 | MATCH_ROOM_MSG_CHANNEL_USER = "SUBSCRIBE:ROOM:MSG:CHANNEL:(%w+):(%w+):(%d+)"
68 | CREATE_ROOM_MSG_CHANNEL_USER = "SUBSCRIBE:ROOM:MSG:CHANNEL:%s:%s:%d"
69 |
70 | --room, cookie
71 | REDIS_RESPONE_ROOM_INFO = "SUBSCRIBE:RESPONE:ROOM:INFO:%s:*"
72 | MATCH_RESPONE_ROOM_INFO = "SUBSCRIBE:RESPONE:ROOM:INFO:(%w+):(%d+)"
73 | CREATE_RESPONE_ROOM_INFO = "SUBSCRIBE:RESPONE:ROOM:INFO:%s:%d"
74 |
75 | MATCH_SERVER_MSG = "SUBSCRIBE:SERVER:MSG:(%d+)"
76 | CREATE_SERVER_MSG = "SUBSCRIBE:SERVER:MSG:%d"
77 |
78 | REDIS_ROOM_MSG_CHANNEL = "SUBSCRIBE:ROOM:MSG:CHANNEL:*"
79 | MATCH_ROOM_MSG_CHANNEL = "SUBSCRIBE:ROOM:MSG:CHANNEL:(%w+)"
80 | CREATE_ROOM_MSG_CHANNEL = "SUBSCRIBE:ROOM:MSG:CHANNEL:%s"
81 |
82 |
83 | SUBSCRIBE_ROOM_DETAIL_RECEIVE = "SUBSCRIBE:ROOM:DETAIL:RECEIVE"
84 |
85 |
86 | CACHE_EXPIRE_TIME_MEMORY = 1
87 | CACHE_EXPIRE_TIME_REDIS = 300
88 |
89 | OB_TYPE_USER = 1;
90 | OB_TYPE_ITEM = 2;
91 | OB_TYPE_EQUIP = 3;
92 | OB_TYPE_ACCOUNT = 4;
93 |
94 | CHAT_CHANNEL_WORLD = 1
95 | CHAT_CHANNEL_UNION = 2
96 | CHAT_CHANNEL_PRIVATE = 3
97 |
98 | -- 定义包裹分组位置
99 | PAGE_ITEM = 2; -- 道具仓库
100 | PAGE_EQUIP = 3; -- 装备
101 |
102 | -- 各分页的最大道具数量
103 | MAX_PAGE_SIZE = {
104 | [PAGE_EQUIP] = 400,
105 | [PAGE_ITEM] = 250,
106 | };
107 |
108 |
109 | BONUS_TYPE_NOSHOW = 0;
110 | BONUS_TYPE_SHOW = 1;
111 |
112 | NO_OPERATION_TIME = 600
113 | USER_STEP_SAVE_TIME = 120
114 |
115 | EVENT_EXP_CHANGE = "EVENT_EXP_CHANGE"
116 | EVENT_LOGIN_OK = "EVENT_LOGIN_OK"
117 | EVENT_ACCOUNT_START_HIBERNATE = "EVENT_ACCOUNT_START_HIBERNATE"
118 | EVENT_ACCOUNT_END_HIBERNATE = "EVENT_ACCOUNT_END_HIBERNATE"
119 | EVENT_ACCOUNT_WAIT_LOGIN = "EVENT_ACCOUNT_WAIT_LOGIN"
120 | EVENT_ACCOUNT_CANCEL_WAIT_LOGIN = "EVENT_ACCOUNT_CANCEL_WAIT_LOGIN"
121 | EVENT_ACCOUNT_OBJECT_CONSTRUCT = "EVENT_ACCOUNT_OBJECT_CONSTRUCT"
122 | EVENT_ACCOUNT_OBJECT_DESTRUCT = "EVENT_ACCOUNT_OBJECT_DESTRUCT"
123 | EVENT_SUCCESS_ACCOUNT_OBJECT_DESTRUCT = "EVENT_SUCCESS_ACCOUNT_OBJECT_DESTRUCT"
124 | EVENT_SUCCESS_ACCOUNT_END_HIBERNATE = "EVENT_SUCCESS_ACCOUNT_END_HIBERNATE"
125 | EVENT_NOTIFY_ACCOUNT_OBJECT_DESTRUCT = "EVENT_NOTIFY_ACCOUNT_OBJECT_DESTRUCT"
126 | EVENT_USER_OBJECT_CONSTRUCT = "EVENT_USER_OBJECT_CONSTRUCT"
127 | EVENT_USER_CONNECTION_LOST = "EVENT_USER_CONNECTION_LOST"
128 |
--------------------------------------------------------------------------------
/src/utils/net_utils.rs:
--------------------------------------------------------------------------------
1 | use td_rlua::{self, lua_State, LuaRead};
2 | use tunm_proto::*;
3 | use std::collections::HashMap;
4 | use LuaUtils;
5 | pub struct NetUtils;
6 |
7 | impl NetUtils {
8 | pub fn lua_read_value(lua: *mut lua_State,
9 | index: i32)
10 | -> Option {
11 | unsafe {
12 | let t = td_rlua::lua_type(lua, index);
13 | let value = match t {
14 | td_rlua::LUA_TBOOLEAN => {
15 | let val: bool = unwrap_or!(LuaRead::lua_read_at_position(lua, index), return None);
16 | Some(Value::from(val))
17 | }
18 | td_rlua::LUA_TNUMBER => {
19 | let val: f64 = unwrap_or!(LuaRead::lua_read_at_position(lua, index), return None);
20 | if val - val.floor() < 0.001 {
21 | Some(Value::from(val as i64))
22 | } else {
23 | Some(Value::from(val as f32))
24 | }
25 | }
26 | td_rlua::LUA_TSTRING => {
27 | if let Some(val) = LuaRead::lua_read_at_position(lua, index) {
28 | Some(Value::Str(val))
29 | } else {
30 | let dst = unwrap_or!(LuaUtils::read_str_to_vec(lua, index), return None);
31 | Some(Value::from(dst))
32 | }
33 | }
34 | td_rlua::LUA_TTABLE => {
35 | if !td_rlua::lua_istable(lua, index) {
36 | return None;
37 | }
38 | let len = td_rlua::lua_rawlen(lua, index);
39 | if len > 0 {
40 | let mut val: Vec = Vec::new();
41 | for i in 1..(len + 1) {
42 | td_rlua::lua_pushnumber(lua, i as f64);
43 | let new_index = if index < 0 {
44 | index - 1
45 | } else {
46 | index
47 | };
48 | td_rlua::lua_gettable(lua, new_index);
49 | let sub_val = NetUtils::lua_read_value(lua,
50 | -1);
51 | if sub_val.is_none() {
52 | return None;
53 | }
54 | val.push(sub_val.unwrap());
55 | td_rlua::lua_pop(lua, 1);
56 | }
57 | Some(Value::from(val))
58 | } else {
59 | let mut val: HashMap = HashMap::new();
60 | td_rlua::lua_pushnil(lua);
61 | let t = if index < 0 {
62 | index - 1
63 | } else {
64 | index
65 | };
66 |
67 | while td_rlua::lua_istable(lua, t) && td_rlua::lua_next(lua, t) != 0 {
68 | let sub_val = unwrap_or!(NetUtils::lua_read_value(lua, -1), return None);
69 | let value = if td_rlua::lua_isnumber(lua, -2) != 0 {
70 | let idx: u32 = unwrap_or!(LuaRead::lua_read_at_position(lua, -2),
71 | return None);
72 | Value::from(idx)
73 | } else {
74 | let key: String = unwrap_or!(LuaRead::lua_read_at_position(lua, -2),
75 | return None);
76 | Value::from(key)
77 | };
78 | val.insert(value, sub_val);
79 | td_rlua::lua_pop(lua, 1);
80 | }
81 | Some(Value::from(val))
82 | }
83 | }
84 | _ => Some(Value::Nil),
85 | };
86 | value
87 | }
88 | }
89 |
90 | pub fn lua_convert_value(lua: *mut lua_State,
91 | index: i32)
92 | -> Option> {
93 | let size = unsafe { td_rlua::lua_gettop(lua) - index + 1 };
94 | let mut val: Vec = Vec::new();
95 | for i in 0..size {
96 | let sub_val = NetUtils::lua_read_value(lua,
97 | i + index);
98 | if sub_val.is_none() {
99 | return None;
100 | }
101 | val.push(sub_val.unwrap());
102 | }
103 | Some(val)
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/redis_wrapper.rs:
--------------------------------------------------------------------------------
1 |
2 | use LuaUtils;
3 | use td_rlua::{self, LuaPush, lua_State, LuaRead};
4 | use libc;
5 | use td_rredis::{Value, RedisError, RedisResult, Cmd, Msg};
6 |
7 | static STATUS_SUFFIX: &'static str = "::STATUS";
8 | static ERROR_SUFFIX: &'static str = "::ERROR";
9 | /// the wrapper for push to lua
10 | pub struct RedisWrapperValue(pub Value);
11 | pub struct RedisWrapperError(pub RedisError);
12 | pub struct RedisWrapperResult(pub RedisResult);
13 | pub struct RedisWrapperMsg(pub Msg);
14 |
15 | pub struct RedisWrapperVecVec(pub Vec>);
16 | pub struct RedisWrapperCmd(pub Cmd);
17 |
18 | impl LuaPush for RedisWrapperValue {
19 | fn push_to_lua(self, lua: *mut lua_State) -> i32 {
20 | match self.0 {
21 | Value::Nil => ().push_to_lua(lua),
22 | Value::Int(val) => (val as u32).push_to_lua(lua),
23 | Value::Data(val) => {
24 | unsafe {
25 | td_rlua::lua_pushlstring(lua, val.as_ptr() as *const libc::c_char, val.len())
26 | };
27 | 1
28 | }
29 | Value::Bulk(mut val) => {
30 | let mut wrapper_val: Vec = vec![];
31 | for v in val.drain(..) {
32 | wrapper_val.push(RedisWrapperValue(v));
33 | }
34 | wrapper_val.push_to_lua(lua)
35 | }
36 | Value::Status(val) => {
37 | let val = val + STATUS_SUFFIX;
38 | val.push_to_lua(lua)
39 | }
40 | Value::Okay => {
41 | let val = "OK".to_string() + STATUS_SUFFIX;
42 | val.push_to_lua(lua)
43 | }
44 | }
45 | }
46 | }
47 |
48 | impl LuaPush for RedisWrapperError {
49 | fn push_to_lua(self, lua: *mut lua_State) -> i32 {
50 | let desc = format!("{}", self.0).to_string() + ERROR_SUFFIX;
51 | desc.push_to_lua(lua)
52 | }
53 | }
54 |
55 | impl LuaPush for RedisWrapperResult {
56 | fn push_to_lua(self, lua: *mut lua_State) -> i32 {
57 | match self.0 {
58 | Ok(val) => RedisWrapperValue(val).push_to_lua(lua),
59 | Err(err) => RedisWrapperError(err).push_to_lua(lua),
60 | }
61 | }
62 | }
63 |
64 | impl LuaPush for RedisWrapperMsg {
65 | fn push_to_lua(self, lua: *mut lua_State) -> i32 {
66 | unsafe {
67 | td_rlua::lua_newtable(lua);
68 |
69 | let payload: RedisResult = self.0.get_payload();
70 | if payload.is_ok() {
71 | "payload".push_to_lua(lua);
72 | RedisWrapperValue(payload.ok().unwrap()).push_to_lua(lua);
73 | td_rlua::lua_settable(lua, -3);
74 | }
75 |
76 | "channel".push_to_lua(lua);
77 | self.0.get_channel_name().push_to_lua(lua);
78 | td_rlua::lua_settable(lua, -3);
79 |
80 | let pattern: RedisResult = self.0.get_pattern();
81 | if pattern.is_ok() {
82 | "pattern".push_to_lua(lua);
83 | pattern.ok().unwrap().push_to_lua(lua);
84 | td_rlua::lua_settable(lua, -3);
85 | }
86 | 1
87 | }
88 | }
89 | }
90 |
91 |
92 | impl LuaRead for RedisWrapperVecVec {
93 | fn lua_read_with_pop_impl(lua: *mut lua_State, index: i32, _pop: i32) -> Option {
94 | let args = unsafe { td_rlua::lua_gettop(lua) - index.abs() + 1 };
95 | let mut vecs = vec![];
96 | if args < 0 {
97 | return None;
98 | }
99 | for i in 0..args {
100 | let mut val: Option> = None;
101 | let bval: Option = LuaRead::lua_read_at_position(lua, i + index);
102 | if let Some(b) = bval {
103 | if b {
104 | val = Some("1".to_string().into_bytes());
105 | } else {
106 | val = Some("0".to_string().into_bytes());
107 | }
108 | }
109 | if val.is_none() {
110 | let dst = unwrap_or!(LuaUtils::read_str_to_vec(lua, i + index), return None);
111 | val = Some(dst);
112 | }
113 | if val.is_none() {
114 | return None;
115 | }
116 | vecs.push(val.unwrap());
117 | }
118 | Some(RedisWrapperVecVec(vecs))
119 | }
120 | }
121 |
122 | impl LuaRead for RedisWrapperCmd {
123 | fn lua_read_with_pop_impl(lua: *mut lua_State, index: i32, _pop: i32) -> Option {
124 | let vecs: RedisWrapperVecVec = unwrap_or!(LuaRead::lua_read_at_position(lua, index),
125 | return None);
126 | let mut cmd = Cmd::new();
127 | cmd.arg(vecs.0);
128 | Some(RedisWrapperCmd(cmd))
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // 使用 IntelliSense 了解相关属性。
3 | // 悬停以查看现有属性的描述。
4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "LuaHelper-Debug",
9 | "request": "launch",
10 | "name": "LuaHelper-Attach",
11 | "description": "通用模式,通常调试项目请选择此模式",
12 | "cwd": "",
13 | "luaFileExtension": "",
14 | "connectionPort": 8818,
15 | "stopOnEntry": true,
16 | "useCHook": true,
17 | "autoPathMode": true
18 | },
19 | {
20 | "type": "lldb",
21 | "request": "launch",
22 | "name": "Debug unit tests in library 'tunm'",
23 | "cargo": {
24 | "args": [
25 | "test",
26 | "--no-run",
27 | "--lib",
28 | "--package=tunm"
29 | ],
30 | "filter": {
31 | "name": "tunm",
32 | "kind": "lib"
33 | }
34 | },
35 | "args": [],
36 | "cwd": "${workspaceFolder}"
37 | },
38 | {
39 | "type": "lldb",
40 | "request": "launch",
41 | "name": "Debug example 'client'",
42 | "cargo": {
43 | "args": [
44 | "build",
45 | "--example=client",
46 | "--package=tunm"
47 | ],
48 | "filter": {
49 | "name": "client",
50 | "kind": "example"
51 | }
52 | },
53 | "args": [],
54 | "cwd": "${workspaceFolder}"
55 | },
56 | {
57 | "type": "lldb",
58 | "request": "launch",
59 | "name": "Debug unit tests in example 'client'",
60 | "cargo": {
61 | "args": [
62 | "test",
63 | "--no-run",
64 | "--example=client",
65 | "--package=tunm"
66 | ],
67 | "filter": {
68 | "name": "client",
69 | "kind": "example"
70 | }
71 | },
72 | "args": [],
73 | "cwd": "${workspaceFolder}"
74 | },
75 | {
76 | "type": "lldb",
77 | "request": "launch",
78 | "name": "Client",
79 | "cargo": {
80 | "args": [
81 | "build",
82 | "--example=server",
83 | "--package=tunm"
84 | ],
85 | "filter": {
86 | "name": "server",
87 | "kind": "example"
88 | }
89 | },
90 | "args": ["-c", "server.yaml", "-s", "D:/game/poker_server/scripts/"],
91 | "cwd": "${workspaceFolder}"
92 | },
93 | {
94 | "type": "lldb",
95 | "request": "launch",
96 | "name": "Debug unit tests in example 'net'",
97 | "cargo": {
98 | "args": [
99 | "test",
100 | "--no-run",
101 | "--example=net",
102 | "--package=tunm"
103 | ],
104 | "filter": {
105 | "name": "net",
106 | "kind": "example"
107 | }
108 | },
109 | "args": [],
110 | "cwd": "${workspaceFolder}"
111 | },
112 | {
113 | "type": "lldb",
114 | "request": "launch",
115 | "name": "Debug example 'server'",
116 | "cargo": {
117 | "args": [
118 | "build",
119 | "--example=server",
120 | "--package=tunm",
121 | ],
122 | "filter": {
123 | "name": "server",
124 | "kind": "example"
125 | }
126 | },
127 | "args": ["-s", "D:/game/poker_server/scripts/"],
128 | "cwd": "${workspaceFolder}"
129 | },
130 | {
131 | "type": "lldb",
132 | "request": "launch",
133 | "name": "Debug unit tests in example 'server'",
134 | "cargo": {
135 | "args": [
136 | "test",
137 | "--no-run",
138 | "--example=server",
139 | "--package=tunm"
140 | ],
141 | "filter": {
142 | "name": "server",
143 | "kind": "example"
144 | }
145 | },
146 | "args": [],
147 | "cwd": "${workspaceFolder}"
148 | }
149 | ]
150 | }
--------------------------------------------------------------------------------
/scripts/global/base/global_agents.lua:
--------------------------------------------------------------------------------
1 | -- global_agents.lua
2 | -- Created by wugd
3 | -- 维护连接对象
4 |
5 | -- 变量定义
6 | local agents = {};
7 | --{1:2} client map or {2:{1=true,3=true}} logic map
8 | local port_map = {};
9 | --{GateType = {1=true,3=true}, LogicType={2=true, 3=true}, ClientType={4=true}}
10 | local type_fds = {};
11 |
12 | local forward_idx = 1
13 | local forwards_unique = {}
14 | setmetatable(forwards_unique, { __mode = "v" });
15 | -- setmetatable(agents, { __mode = "v" });
16 |
17 | -- 定义公共接口,按照字母顺序排序
18 |
19 | -- 取得所有的 agent
20 | function get_all_agents()
21 | return agents;
22 | end
23 |
24 | function get_real_agent_count()
25 | local sum = 0
26 | for port,_ in ipairs(agents) do
27 | if port < 0xFFFF then
28 | sum = sum + 1
29 | end
30 | end
31 | return sum
32 | end
33 |
34 | function get_port_map()
35 | return port_map
36 | end
37 | -- 根据 port_no 找 agent 对象
38 | function find_agent_by_port(port_no)
39 | return agents[port_no];
40 | end
41 |
42 | function reset_port_agent(port_no, agent)
43 | agents[port_no] = agent;
44 | end
45 |
46 | -- 移除 port_no 与 agent 的映射关系
47 | function remove_port_agent(port_no, sended_close)
48 | local agent = agents[port_no]
49 | if agent then
50 | local code_type, code_id = agent:get_code_type()
51 | if code_type == SERVER_TYPE_GATE then
52 | for _,ag in pairs(DUP(agents)) do
53 | if ag:get_server_type() == SERVER_TYPE_CLIENT then
54 | ag:connection_lost()
55 | end
56 | end
57 | elseif not sended_close then
58 | local logic_agent = find_agent_by_port(get_map_port(port_no))
59 | if logic_agent then
60 | logic_agent:send_message(LOSE_CLIENT, port_no, true)
61 | end
62 |
63 | local gate_agent = find_agent_by_port(get_gate_fd())
64 | if gate_agent then
65 | gate_agent:send_message(LOSE_CLIENT, port_no, true)
66 | end
67 | end
68 | remove_port_map(port_no)
69 | -- type_fds[server_type] = type_fds[server_type] or {}
70 | -- type_fds[server_type][port_no] = nil
71 | end
72 | agents[port_no] = nil
73 | end
74 |
75 | -- 设置 port_no 与 agent 的映射关系
76 | function set_port_agent(port_no, agent)
77 | agents[port_no] = agent;
78 | end
79 |
80 | function set_type_port(server_type, port_no)
81 | -- type_fds[server_type] = type_fds[server_type] or {}
82 | -- type_fds[server_type][port_no] = true
83 | end
84 |
85 | function set_code_type_port(code_type, code_id, port_no)
86 | type_fds[code_type] = type_fds[code_type] or {}
87 | type_fds[code_type][code_id] = port_no
88 |
89 | TRACE("set_code_type_port type_fds ==== %o code_type = %o, code_id = %o", type_fds, code_type, code_id)
90 | end
91 |
92 | function remove_port_map(port_no)
93 | local ports = port_map[port_no]
94 | for no, _ in pairs(ports or {}) do
95 | if port_map[no] then
96 | port_map[no][port_no] = nil
97 | end
98 | if IS_EMPTY_TABLE(port_map[no]) then
99 | port_map[no] = nil
100 | end
101 | end
102 | port_map[port_no] = nil
103 | end
104 |
105 | function set_port_map(port_no_server, port_no_client)
106 | port_map[port_no_server] = port_map[port_no_server] or {}
107 | port_map[port_no_client] = port_map[port_no_client] or {}
108 | port_map[port_no_server][port_no_client] = true
109 | port_map[port_no_client][port_no_server] = true
110 | end
111 |
112 | function get_map_port(port_no)
113 | for port,_ in pairs(port_map[port_no] or {}) do
114 | return port
115 | end
116 | return -1
117 | end
118 |
119 | function get_logic_fd()
120 | local logic_fds = type_fds[SERVER_TYPE_LOGIC] or {}
121 | local resultfd = -1
122 | local max = -1
123 | for fd,_ in pairs(logic_fds) do
124 | local size = SIZEOF(port_map[fd])
125 | if max < size then
126 | resultfd = fd
127 | max = size
128 | end
129 | end
130 | return resultfd
131 | end
132 |
133 | function get_gate_fd()
134 | local gate = type_fds[SERVER_TYPE_GATE] or {}
135 | for id,fd in pairs(gate or {}) do
136 | return fd
137 | end
138 | return -1
139 | end
140 |
141 | function find_port_by_code(code_type, code_id)
142 | TRACE("type_fds ==== %o code_type = %o, code_id = %o", type_fds, code_type, code_id)
143 | if not type_fds[code_type] then
144 | return -1
145 | end
146 | return find_agent_by_port(type_fds[code_type][code_id] or -1)
147 | end
148 |
149 | -- 根据 port_no 找 agent 对象
150 | function find_agent_by_forward(port_no)
151 | return forwards_unique[port_no];
152 | end
153 |
154 | function get_agent_forward_map(agent)
155 | local unique = agent:get_forward_unique()
156 | if unique == -1 then
157 | while true do
158 | forward_idx = forward_idx + 1
159 | forward_idx = bit32.band(forward_idx, 0xffffffff);
160 | forward_idx = (forward_idx == 0 and 1 or forward_idx);
161 | if not forwards_unique[forward_idx] then
162 | agent:set_forward_unique(forward_idx)
163 | forwards_unique[forward_idx] = agent
164 | return forward_idx
165 | end
166 | end
167 | end
168 | return 0
169 | end
--------------------------------------------------------------------------------
/scripts/global/daemons/propertyd.lua:
--------------------------------------------------------------------------------
1 | -- propertyd.lua
2 | -- Created by wugd
3 | -- 负责物件模块
4 |
5 | -- 声明模块名
6 | PROPERTY_D = {}
7 | setmetatable(PROPERTY_D, {__index = _G})
8 | local _ENV = PROPERTY_D
9 |
10 | local item_file = "data/txt/ItemInfo.txt"
11 | local equip_file = "data/txt/EquipInfo.txt"
12 | local item_table = {}
13 | local equip_table = {}
14 | local item_fields_list = {}
15 | local equip_fields_list = {}
16 |
17 | -- 相关模块注册回调
18 | local property_callback = {}
19 |
20 | -- 定义内部接口,按照字母顺序排序
21 | local function load_item_table()
22 | item_table = IMPORT_D.readcsv_to_mapping(item_file) or {}
23 |
24 | local item_basic_ob
25 | local name
26 | for class_id, info in pairs(item_table) do
27 |
28 | ASSERT(class_id ~= 0,"item表中有class_id为0的道具")
29 | info = DUP(info)
30 | -- 创建道具基本对象
31 | if not info["ob_type"] then
32 | info["ob_type"] = OB_TYPE_ITEM
33 | end
34 | item_basic_ob = CLONE_OBJECT(ITEM_TDCLS, info)
35 |
36 | set_class_basic_object(class_id, item_basic_ob)
37 | name = info["name"]
38 |
39 | if name then
40 | set_name_basic_object(name, item_basic_ob)
41 | end
42 |
43 | -- 执行注册的回调函数
44 | for _, f in ipairs(property_callback) do
45 | f(info)
46 | end
47 | item_table[class_id] = SET_TABLE_READ_ONLY(info)
48 | end
49 | end
50 |
51 | local function load_equip_table()
52 | equip_table = IMPORT_D.readcsv_to_mapping(equip_file) or {}
53 |
54 | local equip_basic_ob
55 | local name
56 | for class_id, info in pairs(equip_table) do
57 |
58 | ASSERT(class_id ~= 0,"equip表中有class_id为0的道具")
59 | info = DUP(info)
60 | -- 创建装备基本对象
61 | if not info["ob_type"] then
62 | info["ob_type"] = OB_TYPE_EQUIP
63 | end
64 | equip_basic_ob = CLONE_OBJECT(EQUIP_TDCLS, info)
65 | equip_basic_ob:set("amount", 1)
66 |
67 | set_class_basic_object(class_id, equip_basic_ob)
68 | name = info["name"]
69 |
70 | if name then
71 | set_name_basic_object(name, equip_basic_ob)
72 | end
73 |
74 | -- 执行注册的回调函数
75 | for _, f in ipairs(property_callback) do
76 | f(info)
77 | end
78 | equip_table[class_id] = SET_TABLE_READ_ONLY(info)
79 | end
80 | end
81 |
82 |
83 | -- 定义公共接口,按照字母顺序排序
84 |
85 | -- 克隆物件对象
86 | function clone_object_from(class_id, property_info, from_db)
87 | local basic_object = find_basic_object_by_class_id(class_id)
88 | if not basic_object then
89 | -- 没有找到相应的基本对象,不能构造物件
90 | return
91 | end
92 |
93 | -- 保存原来信息
94 | local ori_property_info = DUP(property_info)
95 |
96 | if not property_info["rid"] then
97 | -- 新道具,生成RID
98 | property_info["rid"] = NEW_RID()
99 | end
100 |
101 | -- 设置class_id
102 | property_info["class_id"] = class_id
103 |
104 | -- 设置默认数量
105 | if not property_info["amount"] then
106 | property_info["amount"] = 1
107 | end
108 |
109 | -- 根据不同类型的物件创建对象
110 | local ob_type = property_info["ob_type"]
111 | ob_type = ob_type or basic_object:query("ob_type")
112 |
113 | local property_ob
114 | if ob_type == OB_TYPE_ITEM then
115 | -- 创建道具对象
116 | property_ob = CLONE_OBJECT(ITEM_TDCLS, property_info)
117 | elseif ob_type == OB_TYPE_EQUIP then
118 | property_ob = CLONE_OBJECT(EQUIP_TDCLS, property_info)
119 | ori_property_info["amount"] = nil
120 | if not property_ob:query("lv") then
121 | property_ob:set("lv", 0)
122 | end
123 | if not property_ob:query("exp") then
124 | property_ob:set("exp", 0)
125 | end
126 | end
127 |
128 | if from_db ~= true then
129 | -- 物件不再数据库中,执行物件初始化脚本
130 | -- local init_script = property_ob:query("init_script")
131 | -- if (IS_INT(init_script) and init_script > 0) then
132 | -- INVOKE_SCRIPT(init_script, property_ob, property_ob:query("init_arg"), instance_id)
133 | -- end
134 |
135 | -- 表示该物件不在数据库中,记录标识,以便该物件加载到玩家身上后,
136 | -- 保存玩家数据时,使用 insert 操作而非 update 操作
137 | property_ob:set_temp("not_in_db", true)
138 | end
139 |
140 | -- 防止传入数据被初始化脚本覆盖
141 | property_ob:absorb_dbase(ori_property_info)
142 |
143 | return property_ob
144 | end
145 |
146 | function get_property_info(class_id)
147 | if equip_table[class_id] then
148 | return equip_table[class_id]
149 | elseif item_table[class_id] then
150 | return item_table[class_id]
151 | end
152 | end
153 |
154 | -- 取得指定 class_id 的物件信息
155 | function get_item_dbase(class_id)
156 | if not class_id then
157 | return item_table
158 | else
159 | return item_table[class_id]
160 | end
161 | end
162 |
163 | function get_equip_table(class_id)
164 | if not class_id then
165 | return equip_table
166 | else
167 | return equip_table[class_id]
168 | end
169 | end
170 |
171 | function get_item_or_equip_info(class_id)
172 | ASSERT(class_id and class_id > 0," class_id must > 0")
173 | if item_table[class_id] then
174 | return item_table[class_id]
175 | else
176 | return equip_table[class_id]
177 | end
178 | end
179 |
180 | -- 注册其他模块需要收集的道具信息
181 | function register_property_callback(f)
182 | property_callback[#property_callback + 1] = f
183 | end
184 |
185 | local function init()
186 | -- 加载道具表
187 | load_item_table()
188 | load_equip_table()
189 | end
190 |
191 | -- 模块的入口执行
192 | function create()
193 | register_post_init(init)
194 | end
195 |
196 | create()
197 |
--------------------------------------------------------------------------------
/scripts/server/daemons/login_queued.lua:
--------------------------------------------------------------------------------
1 | -- login_queued.lua
2 | -- Created by wugd
3 | -- 登录排队机制
4 |
5 | -- 声明模块名
6 | LOGIN_QUEUE_D = {}
7 | setmetatable(LOGIN_QUEUE_D, {__index = _G})
8 | local _ENV = LOGIN_QUEUE_D
9 |
10 | -- 服务端负载
11 | local online_limit = 600
12 | local cpu_useage_limit = 85
13 |
14 | -- 登录队列大小限制
15 | local login_size_limit = 30
16 |
17 | -- 需要执行登录的队列
18 | local login_queue = {}
19 |
20 | -- 正在登陆的玩家列表
21 | local login_list = {}
22 | setmetatable(login_list, { __mode = "v" })
23 |
24 |
25 | -- 获得正在登陆的agent
26 | local function get_login_list_size()
27 | local login_size = 0
28 | for _, agent in pairs(login_list) do
29 | if IS_OBJECT(agent) then
30 | login_size = login_size + 1
31 | end
32 | end
33 | return login_size
34 | end
35 |
36 | local function _notify_queue_number()
37 |
38 | -- 弹出无效的登陆数据
39 | local login_data = login_queue:get_first()
40 | if login_data then
41 | if not login_data["agent"] or not login_data["agent"]:is_valid() or
42 | not login_data["agent"]:is_authed() then
43 | login_queue:pop_first()
44 | end
45 | end
46 |
47 | -- 通知客户端排队号数
48 | local agent
49 | local invalid = 0
50 | local index = 0
51 | for _,value in pairs(login_queue:get_data()) do
52 | if type(value) == "table" then
53 | index = index + 1
54 | agent = value["agent"]
55 |
56 | -- 统计无效的agent
57 | if not agent or not agent:is_valid() or not agent:is_authed() then
58 | invalid = invalid + 1
59 | end
60 |
61 | if agent and agent:is_valid() and agent:is_authed() then
62 | agent:send_message(MSG_WAIT_QUEUE_NUMBER,
63 | (k - invalid + 1) + get_login_list_size())
64 | end
65 | end
66 | end
67 | end
68 |
69 | -- 处理登陆队列的登陆
70 | local function _respond_login()
71 |
72 | if not is_server_load_limit() and get_login_list_size() <= login_size_limit then
73 | -- 将客户端请求出队列
74 | local login_data = login_queue:pop_first()
75 | if login_data then
76 | if login_data["agent"]:is_valid() and login_data["agent"]:is_authed()
77 | and type(login_data["func"]) == "function" then
78 |
79 | -- 设置agent为正在登陆的
80 | login_list[login_data["agent"]:get_uni_port_no()] = agent
81 |
82 | -- 执行登录处理
83 | login_data["func"](login_data["agent"], login_data["rid"], login_data["login_info"])
84 | end
85 | end
86 | end
87 | end
88 |
89 | -- 客户端登录请求缓存到队列
90 | function cache_login(agent, rid, login_info, func)
91 | -- 判断连接是否有效
92 | if agent:is_valid() and agent:is_authed() then
93 |
94 | -- 登录信息
95 | local login_data = {
96 | agent = agent,
97 | rid = rid,
98 | login_info = login_info,
99 | func = func,
100 | }
101 |
102 | local is_vip = login_info["is_vip"]
103 | if is_vip == 1 then
104 | -- 如果是vip且服务端不繁忙
105 | if not is_server_load_limit() then
106 |
107 | -- 设置agent为正在登陆的
108 | login_list[agent:get_uni_port_no()] = agent
109 |
110 | -- 执行登录处理
111 | func(agent, rid, login_info)
112 | else
113 | login_queue:push_front(login_data)
114 | end
115 | else
116 |
117 | -- 判断正在登录的请求是否小于登陆限制
118 | if get_login_list_size() <= login_size_limit then
119 |
120 | if not is_server_load_limit() then
121 |
122 | -- 设置agent为正在登陆的
123 | login_list[agent:get_uni_port_no()] = agent
124 |
125 | -- 执行登录处理
126 | func(agent, rid, login_info)
127 | else
128 | login_queue:push_back(login_data)
129 | end
130 | else
131 | login_queue:push_back(login_data)
132 | end
133 | end
134 | end
135 | end
136 |
137 | -- 移除正在登陆的agent
138 | function remove_login_list_agent(port_no)
139 | login_list[port_no] = nil
140 | end
141 |
142 | -- 是否处于服务器负载
143 | function is_server_load_limit()
144 | -- TODO fix Mac cpu avg
145 | -- 判断gs负载情况
146 | local cpu = SYSTEM_D.get_cpu_ratio_avg() or 0
147 | if cpu < cpu_useage_limit then
148 | return false
149 | end
150 |
151 | return false
152 | end
153 |
154 | -- 从登录队列中取出登录请求
155 | function respond_login()
156 |
157 | _respond_login()
158 |
159 | --每隔500ms, 从相应登录请求
160 | set_timer(500, _respond_login, nil, true)
161 | end
162 |
163 | -- 提示排队的数量
164 | function notify_queue_number()
165 |
166 | _notify_queue_number()
167 |
168 | --每隔6000ms, 从相应登录请求
169 | set_timer(6000, _notify_queue_number, nil, true)
170 | end
171 |
172 | function get_online_limit()
173 | return online_limit
174 | end
175 |
176 | function set_online_limit(num)
177 |
178 | if not IS_INT(num) then
179 | TRACE("%o不为数字", num)
180 | return
181 | end
182 |
183 | online_limit = num
184 | end
185 |
186 |
187 | -- 登陆成功 事件处理
188 | local function func_login_ok(user)
189 | remove_login_list_agent(user:get_uni_port_no())
190 | end
191 |
192 | -- 模块的入口执行
193 | function create()
194 | -- 初始化登录队列
195 | login_queue = CLONE_OBJECT(QUEUE_TDCLS)
196 |
197 | -- 响应gs队列中的登录请求
198 | register_post_init(respond_login)
199 |
200 | -- 提示排队的数量
201 | register_post_init(notify_queue_number)
202 |
203 | register_as_audience("LOGIN_QUEUE_D", {EVENT_USER_LOGIN = func_login_ok})
204 | end
205 |
206 | create()
207 |
--------------------------------------------------------------------------------
/scripts/server/cmds/cmd_logic.lua:
--------------------------------------------------------------------------------
1 | --cmd_logic.lua
2 |
3 | -- 登录帐号命令处理
4 | function cmd_login(agent, map)
5 | TRACE("cmd_login receive!!!!!!!!")
6 | LOGIN_D.login(agent, map)
7 | end
8 |
9 | --逻辑服收到新用户登陆消息
10 | function new_client_init(agent, port, data, ext)
11 | --端口区分本地端口
12 | port = port + 0x10000
13 |
14 | --断线重连
15 | local old_agent = find_agent_by_port(port)
16 | if old_agent then
17 | old_agent:connection_lost(true)
18 | end
19 | local client_agent = CLONE_OBJECT(AGENT_TDCLS);
20 | -- 设置端口与 agent 的映射关系
21 | client_agent:set_all_port_no(port, agent:get_port_no())
22 | client_agent:set_client_ip(ext["client_ip"])
23 | if ext.is_websocket then
24 | client_agent:set_websocket(ext.is_websocket)
25 | end
26 | client_agent:set_server_type(SERVER_TYPE_CLIENT)
27 | client_agent:set_authed(true)
28 | end
29 |
30 | --获取用户列表
31 | function cmd_user_list(account)
32 | ACCOUNT_D.get_user_list(account)
33 | end
34 |
35 | function cmd_create_user(account, info)
36 | ACCOUNT_D.request_create_user(account, info)
37 | end
38 |
39 | function cmd_select_user(account, rid)
40 | ACCOUNT_D.request_select_user(account, rid)
41 | end
42 |
43 | function cmd_common_op(user, info)
44 | if info.oper == "add" then
45 | user:add_attrib(info.field, info.amount)
46 | elseif info.oper == "cost" then
47 | user:cost_attrib(info.field, info.amount)
48 | elseif info.oper == "add_item" then
49 | BONUS_D.do_user_bonus(user, {property = { {class_id = info.class_id, amount = info.amount} }}, BONUS_TYPE_SHOW, BONUS_TYPE_SHOW)
50 | end
51 | end
52 |
53 | function cmd_sale_object(user, info)
54 | local rid, amount = info.rid, info.amount
55 | local object = find_object_by_rid(rid)
56 | if not object or get_ob_rid(user) ~= object:query("owner") then
57 | return user:send_message(MSG_SALE_OBJECT, {ret = -1, err_msg = "物品rid有误"})
58 | end
59 |
60 | if object:query("sell_price") == 0 then
61 | return user:send_message(MSG_SALE_OBJECT, {ret = -1, err_msg = "该物品不可出售"})
62 | end
63 |
64 | local sale_amount = math.min(info.amount, object:query("amount"))
65 | user:add_attrib("gold", sale_amount * object:query("sell_price"))
66 | object:cost_amount(sale_amount)
67 | return user:send_message(MSG_SALE_OBJECT, {ret = 0})
68 | end
69 |
70 | function cmd_chat( user, channel, info )
71 | local data = {chat_channel = channel, send_rid = user:query("rid"), recv_rid = info.recv_rid, send_name = user:query("name"), chat_info = {send_content = info.send_content, send_time = os.time()}}
72 | if channel == CHAT_CHANNEL_WORLD then
73 | REDIS_D.run_command("PUBLISH", REDIS_CHAT_CHANNEL_WORLD, ENCODE_JSON(data))
74 | end
75 |
76 | end
77 |
78 | function cmd_enter_room(user, info)
79 | local room = ROOM_D.get_detail_room(info.room_name)
80 | if not room then
81 | user:send_message(MSG_ENTER_ROOM, {ret = -1, err_msg = "房间不存在"})
82 | return
83 | end
84 |
85 | if user:query_temp("room_name") ~= nil then
86 | user:send_message(MSG_ENTER_ROOM, {ret = -2, err_msg = "您已在房间内", room_name = user:query_temp("room_name")})
87 | return
88 | end
89 |
90 | local function enter_room_callback(args, ext)
91 | if not IS_OBJECT(user) then
92 | return
93 | end
94 |
95 | user:set_temp("room_server_id", tonumber(SERVER_ID))
96 | user:set_temp("room_name", info.room_name)
97 | user:send_message(MSG_ENTER_ROOM, {room_name = info.room_name})
98 | end
99 |
100 | --get room type, user base info, room game data
101 | local base_info = DUP(user:query())
102 | if room.game_type == "ddz" then
103 | base_info.ddz_info = user:get_ddz_dbase():query()
104 | end
105 | base_info["server_id"] = tonumber(SERVER_ID)
106 | INTERNAL_COMM_D.send_room_message(info.room_name, get_ob_rid(user), {enter_room_callback, {user = user, info = info}}, CMD_ROOM_MESSAGE, "enter_room", base_info)
107 | end
108 |
109 | function cmd_leave_room(user, info)
110 | local room_name = user:query_temp("room_name")
111 | if not room_name then
112 | user:send_message(MSG_LEAVE_ROOM, {ret = -1, err_msg = "您未在房间内"})
113 | return
114 | end
115 |
116 | local function leave_room_callback(args, ext)
117 | TRACE("leave_room_callback!!!")
118 | if not IS_OBJECT(user) then
119 | return
120 | end
121 | user:send_message(MSG_LEAVE_ROOM, {room_name = room_name})
122 | end
123 |
124 | INTERNAL_COMM_D.send_room_message(room_name, get_ob_rid(user), {leave_room_callback, {}}, CMD_ROOM_MESSAGE, "leave_room", {server_id = tonumber(SERVER_ID)})
125 | user:delete_temp("room_name")
126 | end
127 |
128 | function respone_room_message(user, oper, info)
129 | if oper == "reconnect_user" then
130 | user:send_message(MSG_ROOM_MESSAGE, "pre_room", info)
131 | elseif oper == "calc_score" then
132 | if info.game_type == "ddz" then
133 | local ddz_dbase = user:get_ddz_dbase()
134 | ddz_dbase:calc_score(info)
135 | INTERNAL_COMM_D.send_room_message(info.room_name, get_ob_rid(user), {}, CMD_ROOM_MESSAGE, "detail_info", {ddz_info = ddz_dbase:query()})
136 | end
137 | end
138 | end
139 |
140 | function cmd_room_oper(user, oper, info)
141 | if oper == "enter_room" then
142 | cmd_enter_room(user, info)
143 | elseif oper == "leave_room" then
144 | cmd_leave_room(user, info)
145 | elseif oper == "detail_room" then
146 | local detail = TABLE_VALUE_TO_ARRAY(ROOM_D.get_room_detail())
147 | user:send_message(MSG_ROOM_OPER, oper, {map_list = detail})
148 | else
149 | end
150 | end
151 |
152 |
--------------------------------------------------------------------------------