├── 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 | [![Build Status](https://travis-ci.org/tickbh/tunm.svg?branch=master)](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 | --------------------------------------------------------------------------------