├── .gitignore ├── .idea ├── GoYCServer.iml ├── dataSources.local.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── README.md ├── bin └── config │ └── node_cfg.json ├── engine ├── BaseModule │ └── NetModule │ │ ├── API.go │ │ └── Net.go ├── YAoi │ ├── Aoi.go │ ├── AoiManager.go │ ├── AoiManager_test.go │ ├── GoAoi.go │ ├── GoAoiManager.go │ ├── GoNineGridAoi.go │ ├── GoNineGridAoiManager.go │ ├── GoTowerAoi.go │ └── GoTowerAoiManager.go ├── YAttr │ ├── Attribute.go │ └── AttributeTemplate.go ├── YConfig │ └── Config.go ├── YConsul │ └── Consul.md ├── YDecode │ └── Decode.go ├── YEntity │ ├── Entity.go │ ├── Info.go │ └── entity_test.go ├── YJson │ └── json.go ├── YLog │ └── Log.go ├── YModule │ ├── Info.go │ └── Module.go ├── YMsg │ └── in_msg.go ├── YNet │ ├── Connect.go │ ├── Listen.go │ ├── Message.go │ ├── NetMsgManager.go │ ├── NetMsgPack.go │ └── Session.go ├── YNode │ ├── API.go │ ├── Info.go │ └── Node.go ├── YPathFinding │ ├── AStar.go │ ├── AStar_test.go │ └── GoAStarManager.go ├── YTimer │ ├── Timer.go │ ├── TimerAPI.go │ ├── WheelTimer.go │ └── timer_test.go └── YTool │ ├── DebugPrint.go │ ├── position.go │ ├── queue.go │ ├── reactangle.go │ ├── syncqueue.go │ ├── timeter.go │ ├── tool.go │ ├── tools_test.go │ └── uidFactory.go ├── examples ├── AoiAstarExample │ ├── Client │ │ ├── Client.go │ │ ├── Game.go │ │ └── Map.go │ ├── Msg │ │ └── msg.go │ └── Server │ │ ├── Logic │ │ ├── Aoi │ │ │ ├── Aoi.go │ │ │ ├── AoiManager.go │ │ │ ├── AoiManager_test.go │ │ │ ├── GoAoi.go │ │ │ ├── GoAoiManager.go │ │ │ ├── GoNineGridAoi.go │ │ │ └── GoNineGridAoiManager.go │ │ └── Move │ │ │ └── MoveControl.go │ │ ├── Main.go │ │ └── Module │ │ ├── Map │ │ ├── Map.go │ │ └── README.md │ │ ├── MapManager │ │ └── MapManager.go │ │ ├── README.md │ │ └── UserManager │ │ ├── UserInfo.go │ │ └── UserManager.go ├── NetExample │ ├── Client │ │ └── Main.go │ ├── Msg │ │ └── Msg.go │ └── Server │ │ ├── Logic │ │ ├── TestModule │ │ │ └── TestModule.go │ │ └── TestModule2 │ │ │ └── TestModule.go │ │ └── Main.go ├── RPCCallListExample │ ├── Logic │ │ ├── TestModule │ │ │ ├── TestModule.go │ │ │ └── module_test.go │ │ └── TestModule2 │ │ │ └── TestModule.go │ └── Main.go ├── RPCExample │ ├── Logic │ │ ├── TestModule │ │ │ ├── TestModule.go │ │ │ └── module_test.go │ │ └── TestModule2 │ │ │ └── TestModule.go │ └── Main.go └── SeamlessExample │ ├── Client │ ├── Client.go │ ├── Game.go │ └── Map.go │ ├── Msg │ └── msg.go │ └── Server │ ├── Logic │ └── Move │ │ └── MoveControl.go │ ├── Main.go │ ├── Module │ ├── Map │ │ ├── API.go │ │ ├── Info.go │ │ └── Map.go │ ├── MapManager │ │ └── MapManager.go │ ├── README.md │ └── UserManager │ │ ├── UserInfo.go │ │ └── UserManager.go │ ├── ModuleRegister.go │ ├── Util │ └── Map.go │ ├── 无缝地图切换方案.md │ ├── 无缝地图动态创建方案.md │ └── 无缝地图边缘处理方案.md ├── go.mod └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | log -------------------------------------------------------------------------------- /.idea/GoYCServer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/dataSources.local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 项目目的 2 | 3 | - 实现一个基于无缝大地图模式的分布式`MMO`服务器项目 4 | - 实现`MMO`常有的游戏功能,包括技能,怪物,AI,副本,玩家间交互等内容 5 | - 希望以此能找到相关的工作 6 | 7 | ### 项目目标 8 | 9 | - 尽可能利用多核能力来提升效率 10 | - 逻辑尽可能的简单,便于后续维护 11 | - 不追求极致的性能,一切以提升开发效率优先 12 | 13 | #### 演示视频地址 14 | 15 | ``` 16 | https://www.bilibili.com/video/BV1F44y1h7us 17 | ``` 18 | 19 | #### 无缝地图演示 20 | 21 | ``` 22 | https://www.bilibili.com/video/BV1Xq4y1o7S1 23 | ``` 24 | 25 | #### todo: 26 | 27 | ``` 28 | 1.重构框架底层架构,修改成无缝地图模式 (完成) 29 | 2.增加基对象Entity 30 | 3.增加战斗系统 31 | 4.增加怪物系统 32 | 5.增加道具系统 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /bin/config/node_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "CfgList": [ 3 | { 4 | "NodeID": 1, 5 | "Port": 20000, 6 | "PprofPort": 8000, 7 | "Modules":[ 8 | "MapManager", 9 | "UserManager" 10 | ] 11 | }, 12 | { 13 | "NodeID": 2, 14 | "Port": 20012, 15 | "PprofPort": 8012, 16 | "Modules":[] 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /engine/BaseModule/NetModule/API.go: -------------------------------------------------------------------------------- 1 | package NetModule 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNet" 7 | ) 8 | 9 | func (m *NetModule) RPC_Close(s_ uint64) { 10 | _session := m.m_session_pool[s_] 11 | if _session == nil { 12 | return 13 | } 14 | _session.Close() 15 | delete(m.m_session_pool, s_) 16 | } 17 | 18 | func (m *NetModule) RPC_Listen(ip_port_ string) { 19 | err := YNet.ListenTcp4(ip_port_) 20 | if err != nil { 21 | panic(" ListenTcp4 err") 22 | } 23 | ylog.Info("[NetModule] Start Listen [%v]", ip_port_) 24 | } 25 | 26 | func (m *NetModule) RPC_Connect(ip_port_ string) { 27 | go func() { 28 | _new_connect := YNet.NewConnect() 29 | _new_connect.Connect(ip_port_) 30 | _conn_sesstion := _new_connect.GetSession() 31 | _conn_sesstion.StartLoop() 32 | 33 | _msg_pack := YNet.NewNetMsgPack() 34 | _msg_pack.M_msg_name = ip_port_ 35 | _conn_msg := YNet.NewMessage(YNet.NET_SESSION_STATE_CONNECT_OTHER_SUCCESS, _conn_sesstion, _msg_pack) 36 | YNet.G_net_msg_chan <- _conn_msg 37 | }() 38 | } 39 | 40 | func (m *NetModule) RPC_SendNetMsgJson(s_ uint64, msg_ *YNet.NetMsgPack) { 41 | _session := m.m_session_pool[s_] 42 | if _session == nil { 43 | return 44 | } 45 | _session.Send(msg_) 46 | } 47 | 48 | func (m *NetModule) RPC_NetMsgRegister(msg_list_ []string, agent_ YMsg.Agent) { 49 | for _, _msg_it := range msg_list_ { 50 | _, exists := m.m_net_msg_pool[_msg_it] 51 | if !exists { 52 | m.m_net_msg_pool[_msg_it] = make([]YMsg.Agent, 0) 53 | } 54 | m.m_net_msg_pool[_msg_it] = append(m.m_net_msg_pool[_msg_it], agent_) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /engine/BaseModule/NetModule/Net.go: -------------------------------------------------------------------------------- 1 | package NetModule 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YModule" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNet" 7 | "github.com/yxinyi/YCServer/engine/YNode" 8 | "time" 9 | ) 10 | 11 | type NetModule struct { 12 | YModule.BaseInter 13 | m_session_pool map[uint64]*YNet.Session 14 | m_net_msg_pool map[string][]YMsg.Agent 15 | } 16 | 17 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 18 | _info := &NetModule{ 19 | m_session_pool: make(map[uint64]*YNet.Session), 20 | m_net_msg_pool: make(map[string][]YMsg.Agent), 21 | } 22 | _info.Info = YModule.NewInfo(node_) 23 | return _info 24 | } 25 | func (m *NetModule) Init() { 26 | m.Info.Init(m) 27 | } 28 | 29 | 30 | 31 | func (m *NetModule) Loop_100(time time.Time) { 32 | for more := true; more; { 33 | select { 34 | case _msg := <-YNet.G_net_msg_chan: 35 | switch _msg.M_msg_type { 36 | case YNet.NET_SESSION_STATE_CONNECT: 37 | m.m_session_pool[_msg.M_session.GetUID()] = _msg.M_session 38 | case YNet.NET_SESSION_STATE_CONNECT_OTHER_SUCCESS: 39 | _ip_port := _msg.M_net_msg.M_msg_name 40 | m.m_session_pool[_msg.M_session.GetUID()] = _msg.M_session 41 | m.Info.RPCCall(YMsg.ToAgent("YNode"), "RegisterOtherNode", _ip_port, _msg.M_session.GetUID()) 42 | case YNet.NET_SESSION_STATE_MSG: 43 | for _, _agent_it := range m.m_net_msg_pool[_msg.M_net_msg.M_msg_name] { 44 | _net_msg := &YMsg.C2S_net_msg{ 45 | _agent_it, 46 | _msg.M_session.GetUID(), 47 | _msg.M_net_msg, 48 | } 49 | m.NetToOther(_net_msg) 50 | } 51 | case YNet.NET_SESSION_STATE_CLOSE: 52 | delete(m.m_session_pool, _msg.M_session.GetUID()) 53 | } 54 | default: 55 | more = false 56 | } 57 | } 58 | } 59 | 60 | func (m *NetModule) Close() { 61 | YNet.Stop() 62 | } 63 | -------------------------------------------------------------------------------- /engine/YAoi/Aoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | type AoiCell struct { 4 | m_watch_list map[uint64]map[uint64]struct{} 5 | M_enter_callback AoiEnterCallBack 6 | M_quit_callback AoiQuitCallBack 7 | M_move_callback AoiMoveCallBack 8 | M_add_watch_callback AoiAddWatch 9 | } 10 | 11 | func NewAoiCell() *AoiCell { 12 | _cell := &AoiCell{ 13 | m_watch_list: make(map[uint64]map[uint64]struct{}), 14 | } 15 | 16 | return _cell 17 | } 18 | 19 | func (cell *AoiCell) enterCell(enter_ uint64) { 20 | cell.m_watch_list[enter_] = make(map[uint64]struct{}) 21 | } 22 | func (cell *AoiCell) notifyEnterCell(enter_ uint64) { 23 | for _it := range cell.m_watch_list { 24 | cell.M_enter_callback(_it, enter_) 25 | cell.M_enter_callback(enter_,_it) 26 | } 27 | } 28 | 29 | func (cell *AoiCell) quitCell(quit uint64) { 30 | delete(cell.m_watch_list, quit) 31 | } 32 | func (cell *AoiCell) notifyQuitCell(quit_ uint64) { 33 | for _it := range cell.m_watch_list { 34 | cell.M_quit_callback(_it, quit_) 35 | cell.M_quit_callback(quit_, _it) 36 | } 37 | } 38 | 39 | func (cell *AoiCell) updateCell(enter_ uint64) { 40 | for _it := range cell.m_watch_list { 41 | cell.M_move_callback(_it, enter_) 42 | cell.M_move_callback(enter_,_it) 43 | } 44 | } 45 | 46 | /*func (cell *AoiCell) watchTower(enter_ uint32) { 47 | cell.m_obj_list[enter_] = make(map[uint32]struct{}) 48 | for _it := range cell.m_obj_list { 49 | if cell.M_add_watch_callback(enter_, _it) { 50 | cell.m_obj_list[enter_][_it] = struct{}{} 51 | cell.M_enter_callback(enter_, _it) 52 | } 53 | if cell.M_add_watch_callback(_it, enter_) { 54 | cell.m_obj_list[_it][enter_] = struct{}{} 55 | cell.M_enter_callback(_it, enter_) 56 | } 57 | } 58 | } 59 | 60 | func (cell *AoiCell) quitTower(enter_ uint32) { 61 | _watch_list := cell.m_obj_list[enter_] 62 | for _it := range _watch_list { 63 | if enter_== _it { 64 | delete(cell.m_obj_list[_it], enter_) 65 | continue 66 | } 67 | cell.M_quit_callback(enter_, _it) 68 | _, exists := cell.m_obj_list[_it][enter_] 69 | if exists { 70 | cell.M_quit_callback(_it, enter_) 71 | } 72 | delete(cell.m_obj_list[_it], enter_) 73 | } 74 | delete(cell.m_obj_list, enter_) 75 | } 76 | 77 | func (cell *AoiCell) updateCell(enter_ uint32) { 78 | for _it := range cell.m_obj_list { 79 | if cell.M_add_watch_callback(enter_, _it) { 80 | _, exists := cell.m_obj_list[enter_][_it] 81 | if exists { 82 | cell.M_move_callback(enter_, _it) 83 | } else { 84 | cell.m_obj_list[enter_][_it] = struct{}{} 85 | cell.M_enter_callback(enter_, _it) 86 | } 87 | 88 | } else { 89 | _, exists := cell.m_obj_list[enter_][_it] 90 | if exists { 91 | cell.M_quit_callback(enter_, _it) 92 | delete(cell.m_obj_list[enter_], _it) 93 | } 94 | } 95 | 96 | if cell.M_add_watch_callback(_it, enter_) { 97 | _, exists := cell.m_obj_list[_it][enter_] 98 | if exists { 99 | cell.M_move_callback(_it, enter_) 100 | } else { 101 | cell.m_obj_list[_it][enter_] = struct{}{} 102 | cell.M_enter_callback(_it, enter_) 103 | } 104 | } else { 105 | _, exists := cell.m_obj_list[_it][enter_] 106 | if exists { 107 | cell.M_quit_callback(_it, enter_) 108 | delete(cell.m_obj_list[_it], enter_) 109 | } 110 | } 111 | 112 | } 113 | }*/ 114 | -------------------------------------------------------------------------------- /engine/YAoi/AoiManager.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | ) 6 | 7 | type AoiMoveCallBack func(move_, tar_ uint64) 8 | type AoiEnterCallBack func(move_, tar_ uint64) 9 | type AoiQuitCallBack func(move_, tar_ uint64) 10 | type AoiAddWatch func(move_, tar_ uint64) bool 11 | 12 | type AoiManager struct { 13 | M_height float64 14 | M_width float64 15 | m_aoi_list map[uint32]*AoiCell 16 | M_current_index map[uint64]uint32 17 | m_block_height float64 18 | m_block_width float64 19 | m_block_size float64 20 | } 21 | 22 | func NewAoiManager(width_, height_, block_size_ float64) *AoiManager { 23 | _mgr := &AoiManager{ 24 | m_aoi_list: make(map[uint32]*AoiCell), 25 | M_current_index: make(map[uint64]uint32), 26 | } 27 | _mgr.M_height = height_ 28 | _mgr.M_width = width_ 29 | _mgr.m_block_height = height_ / block_size_ 30 | _mgr.m_block_width = width_ / block_size_ 31 | _mgr.m_block_size = block_size_ 32 | return _mgr 33 | } 34 | 35 | func (mgr *AoiManager) Init(add_watch_call_ AoiAddWatch, move_call_ AoiMoveCallBack, enter_call_ AoiEnterCallBack, quit_call_ AoiQuitCallBack) { 36 | for _row_idx := uint32(0); _row_idx < uint32(mgr.m_block_size); _row_idx++ { 37 | for _col_idx := uint32(0); _col_idx < uint32(mgr.m_block_size); _col_idx++ { 38 | _cell := NewAoiCell() 39 | _cell.M_move_callback = move_call_ 40 | _cell.M_enter_callback = enter_call_ 41 | _cell.M_quit_callback = quit_call_ 42 | _cell.M_add_watch_callback = add_watch_call_ 43 | mgr.m_aoi_list[mgr.buildIndex(_row_idx, _col_idx)] = _cell 44 | } 45 | } 46 | } 47 | 48 | /*func getDiff(lhs_ map[uint32]struct{}, rhs_ map[uint32]struct{}) map[uint32]struct{} { 49 | _ret := make(map[uint32]struct{}) 50 | for _it := range lhs_ { 51 | _ret[_it] = struct{}{} 52 | } 53 | for _it := range rhs_ { 54 | delete(_ret, _it) 55 | } 56 | 57 | return _ret 58 | }*/ 59 | 60 | func (mgr *AoiManager) Enter(enter_ uint64, pos_ YTool.PositionXY) { 61 | _current_index := mgr.CalcIndex(pos_) 62 | _cell := mgr.m_aoi_list[_current_index] 63 | _cell.enterCell(enter_) 64 | _round_arr := mgr.getRoundBlock(_current_index) 65 | for _it := range _round_arr { 66 | _cell, exists := mgr.m_aoi_list[_it] 67 | if exists { 68 | _cell.notifyEnterCell(enter_) 69 | } 70 | } 71 | mgr.M_current_index[enter_] = _current_index 72 | } 73 | 74 | func (mgr *AoiManager) Quit(quit_ uint64, pos_ YTool.PositionXY) { 75 | _current_index := mgr.CalcIndex(pos_) 76 | _cell := mgr.m_aoi_list[_current_index] 77 | _cell.quitCell(quit_) 78 | _round_arr := mgr.getRoundBlock(_current_index) 79 | for _it := range _round_arr { 80 | _cell, exists := mgr.m_aoi_list[_it] 81 | if exists { 82 | _cell.notifyQuitCell(quit_) 83 | } 84 | } 85 | delete(mgr.M_current_index, quit_) 86 | } 87 | 88 | func (mgr *AoiManager) Move(move_ uint64, pos_ YTool.PositionXY) { 89 | 90 | _old_round_arr := mgr.getOldRoundBlock(move_) 91 | 92 | _current_index := mgr.CalcIndex(pos_) 93 | _new_round_arr := mgr.getRoundBlock(_current_index) 94 | 95 | if _current_index != mgr.M_current_index[move_] { 96 | _enter_cell := mgr.m_aoi_list[_current_index] 97 | _enter_cell.enterCell(move_) 98 | 99 | } 100 | _enter_cell := YTool.GetSetUint32Diff(_new_round_arr, _old_round_arr) 101 | for _it := range _enter_cell { 102 | _cell, exists := mgr.m_aoi_list[_it] 103 | if exists { 104 | _cell.notifyEnterCell(move_) 105 | } 106 | } 107 | 108 | _update_cell := YTool.GetSetUint32Diff(_new_round_arr, _enter_cell) 109 | for _it := range _update_cell { 110 | _cell, exists := mgr.m_aoi_list[_it] 111 | if exists { 112 | _cell.updateCell(move_) 113 | } 114 | } 115 | if _current_index != mgr.M_current_index[move_] { 116 | _quit_cell := mgr.m_aoi_list[mgr.M_current_index[move_]] 117 | _quit_cell.quitCell(move_) 118 | } 119 | _quit_cell := YTool.GetSetUint32Diff(_old_round_arr, _new_round_arr) 120 | for _it := range _quit_cell { 121 | _cell, exists := mgr.m_aoi_list[_it] 122 | if exists { 123 | _cell.notifyQuitCell(move_) 124 | } 125 | } 126 | mgr.M_current_index[move_] = _current_index 127 | 128 | } 129 | 130 | func (mgr *AoiManager) CalcIndex(xy_ YTool.PositionXY) uint32 { 131 | return mgr.buildIndex(uint32(xy_.M_x/mgr.m_block_width), uint32(xy_.M_y/mgr.m_block_height)) 132 | } 133 | 134 | func (mgr *AoiManager) buildIndex(row_, col_ uint32) uint32 { 135 | return row_ + col_*uint32(mgr.m_block_size) 136 | } 137 | 138 | func (mgr *AoiManager) getOldRoundBlock(uid_ uint64) map[uint32]struct{} { 139 | _old_index := mgr.M_current_index[uid_] 140 | return mgr.getRoundBlock(_old_index) 141 | } 142 | 143 | func (mgr *AoiManager) getRoundBlock(cent_index_ uint32) map[uint32]struct{} { 144 | _ret_round := make(map[uint32]struct{}) 145 | _cent_idex := int(cent_index_) 146 | _block_size := int(mgr.m_block_size) 147 | 148 | _max_idx := int(mgr.m_block_size * mgr.m_block_size) 149 | 150 | _cent_row := int(cent_index_ / uint32(mgr.m_block_size)) 151 | _ret_round[cent_index_] = struct{}{} 152 | { 153 | _left_up := _cent_idex - _block_size - 1 154 | if _left_up >= 0 && (_left_up/_block_size+1) == _cent_row { 155 | _ret_round[uint32(_left_up)] = struct{}{} 156 | } 157 | } 158 | 159 | { 160 | _up := _cent_idex - _block_size 161 | if _up >= 0 && (_up/_block_size+1) == _cent_row { 162 | _ret_round[uint32(_up)] = struct{}{} 163 | } 164 | } 165 | { 166 | _up_right := _cent_idex - _block_size + 1 167 | if _up_right >= 0 && (_up_right/_block_size+1) == _cent_row { 168 | _ret_round[uint32(_up_right)] = struct{}{} 169 | } 170 | } 171 | 172 | { 173 | _left := _cent_idex - 1 174 | if _left >= 0 && (_left/_block_size) == _cent_row { 175 | _ret_round[uint32(_left)] = struct{}{} 176 | } 177 | } 178 | { 179 | _right := _cent_idex + 1 180 | if _right >= 0 && (_right/_block_size) == _cent_row { 181 | _ret_round[uint32(_right)] = struct{}{} 182 | } 183 | } 184 | 185 | { 186 | _down_left := _cent_idex + _block_size - 1 187 | if _down_left < _max_idx && (_down_left/_block_size-1) == _cent_row { 188 | _ret_round[uint32(_down_left)] = struct{}{} 189 | } 190 | } 191 | 192 | { 193 | _down := _cent_idex + _block_size 194 | if _down < _max_idx && (_down/_block_size-1) == _cent_row { 195 | _ret_round[uint32(_down)] = struct{}{} 196 | } 197 | } 198 | { 199 | _down_right := _cent_idex + _block_size + 1 200 | if _down_right < _max_idx && (_down_right/_block_size-1) == _cent_row { 201 | _ret_round[uint32(_down_right)] = struct{}{} 202 | } 203 | } 204 | 205 | return _ret_round 206 | } 207 | -------------------------------------------------------------------------------- /engine/YAoi/AoiManager_test.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | "testing" 6 | ) 7 | 8 | func roundTestHelp(cent_index_, block_ uint32, round_target_ []uint32) map[uint32]struct{} { 9 | _mgr := NewAoiManager(1280, 720, float64(block_)) 10 | _sure_arr := make(map[uint32]struct{}) 11 | for _, _it := range round_target_ { 12 | _sure_arr[_it] = struct{}{} 13 | } 14 | _round_arr := _mgr.getRoundBlock(cent_index_) 15 | for _it := range _round_arr { 16 | _, exists := _sure_arr[_it] 17 | if !exists { 18 | _sure_arr[_it] = struct{}{} 19 | } 20 | delete(_sure_arr, _it) 21 | } 22 | return _sure_arr 23 | } 24 | 25 | func TestAoiGetRoundIndex(t *testing.T) { 26 | 27 | _err_list := roundTestHelp(16, 5, []uint32{10, 11, 12, 15, 17, 20, 21, 22}) 28 | if len(_err_list) > 0 { 29 | t.Fatalf("[%v]", _err_list) 30 | } 31 | 32 | _err_list = roundTestHelp(0, 5, []uint32{1, 5, 6}) 33 | if len(_err_list) > 0 { 34 | t.Fatalf("[%v]", _err_list) 35 | } 36 | 37 | _err_list = roundTestHelp(4, 5, []uint32{3, 8, 9}) 38 | if len(_err_list) > 0 { 39 | t.Fatalf("[%v]", _err_list) 40 | } 41 | 42 | _err_list = roundTestHelp(2, 5, []uint32{1, 6, 7, 8, 3}) 43 | if len(_err_list) > 0 { 44 | t.Fatalf("[%v]", _err_list) 45 | } 46 | 47 | _err_list = roundTestHelp(20, 5, []uint32{15, 16, 21}) 48 | if len(_err_list) > 0 { 49 | t.Fatalf("[%v]", _err_list) 50 | } 51 | _err_list = roundTestHelp(22, 5, []uint32{16, 17, 18, 21, 23}) 52 | if len(_err_list) > 0 { 53 | t.Fatalf("[%v]", _err_list) 54 | } 55 | _err_list = roundTestHelp(24, 5, []uint32{18, 19, 23}) 56 | if len(_err_list) > 0 { 57 | t.Fatalf("[%v]", _err_list) 58 | } 59 | } 60 | 61 | func TestAoiGetRoundIndex10(t *testing.T) { 62 | 63 | _err_list := roundTestHelp(0, 10, []uint32{1, 10, 11}) 64 | if len(_err_list) > 0 { 65 | t.Fatalf("[%v]", _err_list) 66 | } 67 | 68 | _err_list = roundTestHelp(11, 10, []uint32{0, 1, 2, 10, 11, 12, 20, 21, 22}) 69 | if len(_err_list) > 0 { 70 | t.Fatalf("[%v]", _err_list) 71 | } 72 | } 73 | 74 | func CalcIndexHelp(x_, y_ float64, tar_idx_ uint32) bool { 75 | _mgr := NewAoiManager(1280, 720, float64(10)) 76 | _cal_idx := _mgr.CalcIndex(YTool.PositionXY{x_, y_}) 77 | return _cal_idx == tar_idx_ 78 | } 79 | func TestCalcIndex(t *testing.T) { 80 | if !CalcIndexHelp(0,0,0){ 81 | t.Fatal() 82 | } 83 | if !CalcIndexHelp(128,0,1){ 84 | t.Fatal() 85 | } 86 | if !CalcIndexHelp(128,73,11){ 87 | t.Fatal() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /engine/YAoi/GoAoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | ) 6 | 7 | type GoAoiCellAction struct { 8 | m_action uint32 9 | m_action_obj GoAoiObj 10 | } 11 | 12 | const ( 13 | GO_AOI_CELL_ACTION_ENTER = iota 14 | GO_AOI_CELL_ACTION_NOTIFY_ENTER 15 | GO_AOI_CELL_ACTION_UPDATE 16 | GO_AOI_CELL_ACTION_NOTIFY_QUIT 17 | GO_AOI_CELL_ACTION_QUIT 18 | ) 19 | 20 | type GoAoiCell struct { 21 | m_obj_list map[uint64]GoAoiObj 22 | m_mgr_chan *YTool.SyncQueue 23 | M_obj_action chan GoAoiCellAction 24 | m_close chan struct{} 25 | } 26 | 27 | func NewGoAoiCell(mgr_chan_ *YTool.SyncQueue) *GoAoiCell { 28 | _cell := &GoAoiCell{ 29 | m_obj_list: make(map[uint64]GoAoiObj), 30 | m_mgr_chan: mgr_chan_, 31 | M_obj_action: make(chan GoAoiCellAction, 1000), 32 | m_close: make(chan struct{}), 33 | } 34 | go func() { 35 | for { 36 | select { 37 | case <-_cell.M_obj_action: 38 | for _action := range _cell.M_obj_action { 39 | if len(_cell.M_obj_action) == 0 { 40 | break 41 | } 42 | switch _action.m_action { 43 | case GO_AOI_CELL_ACTION_ENTER: 44 | _cell.enterCell(_action.m_action_obj) 45 | case GO_AOI_CELL_ACTION_NOTIFY_ENTER: 46 | _cell.notifyEnterCell(_action.m_action_obj) 47 | case GO_AOI_CELL_ACTION_UPDATE: 48 | _cell.updateCell(_action.m_action_obj) 49 | case GO_AOI_CELL_ACTION_NOTIFY_QUIT: 50 | _cell.notifyQuitCell(_action.m_action_obj) 51 | case GO_AOI_CELL_ACTION_QUIT: 52 | _cell.quitCell(_action.m_action_obj) 53 | } 54 | } 55 | 56 | case <-_cell.m_close: 57 | return 58 | } 59 | } 60 | }() 61 | return _cell 62 | } 63 | 64 | func (cell *GoAoiCell) EnterCell(enter_ GoAoiObj) { 65 | cell.M_obj_action <- GoAoiCellAction{ 66 | GO_AOI_CELL_ACTION_ENTER, 67 | enter_, 68 | } 69 | } 70 | func (cell *GoAoiCell) NotifyEnterCell(enter_ GoAoiObj) { 71 | cell.M_obj_action <- GoAoiCellAction{ 72 | GO_AOI_CELL_ACTION_NOTIFY_ENTER, 73 | enter_, 74 | } 75 | } 76 | func (cell *GoAoiCell) QuitCell(quit_ GoAoiObj) { 77 | cell.M_obj_action <- GoAoiCellAction{ 78 | GO_AOI_CELL_ACTION_QUIT, 79 | quit_, 80 | } 81 | } 82 | func (cell *GoAoiCell) NotifyQuitCell(enter_ GoAoiObj) { 83 | cell.M_obj_action <- GoAoiCellAction{ 84 | GO_AOI_CELL_ACTION_NOTIFY_QUIT, 85 | enter_, 86 | } 87 | } 88 | func (cell *GoAoiCell) UpdateCell(enter_ GoAoiObj) { 89 | cell.M_obj_action <- GoAoiCellAction{ 90 | GO_AOI_CELL_ACTION_UPDATE, 91 | enter_, 92 | } 93 | } 94 | 95 | func (cell *GoAoiCell) enterCell(enter_ GoAoiObj) { 96 | cell.m_obj_list[enter_.M_uid] = enter_ 97 | /* _, exists := cell.m_obj_list[enter_.M_module_uid] 98 | if !exists { 99 | cell.m_obj_list[enter_.M_module_uid] = make(map[uint64]struct{}) 100 | }*/ 101 | } 102 | 103 | func (cell *GoAoiCell) notifyEnterCell(enter_ GoAoiObj) { 104 | _func := func(notify_, action_ GoAoiObj) { 105 | if notify_.PositionXY.Distance(&action_.PositionXY) < notify_.M_view_range { 106 | //cell.m_obj_list[notify_.M_module_uid][action_.M_module_uid] = struct{}{} 107 | cell.m_mgr_chan.Add(GoAoiAction{ 108 | GO_AOI_ACTION_ENTER, 109 | notify_.M_uid, 110 | action_.M_uid, 111 | }) 112 | } 113 | } 114 | for _, _it := range cell.m_obj_list { 115 | _func(_it, enter_) 116 | _func(enter_, _it) 117 | } 118 | } 119 | 120 | func (cell *GoAoiCell) quitCell(quit_ GoAoiObj) { 121 | 122 | delete(cell.m_obj_list, quit_.M_uid) 123 | } 124 | 125 | func (cell *GoAoiCell) notifyQuitCell(quit_ GoAoiObj) { 126 | for _, _it := range cell.m_obj_list { 127 | cell.m_mgr_chan.Add(GoAoiAction{ 128 | GO_AOI_ACTION_QUIT, 129 | quit_.M_uid, 130 | _it.M_uid, 131 | }) 132 | cell.m_mgr_chan.Add(GoAoiAction{ 133 | GO_AOI_ACTION_QUIT, 134 | _it.M_uid, 135 | quit_.M_uid, 136 | }) 137 | } 138 | } 139 | 140 | func (cell *GoAoiCell) updateCell(enter_ GoAoiObj) { 141 | _func := func(notify_, action_ GoAoiObj) { 142 | _, exists := cell.m_obj_list[enter_.M_uid] 143 | if exists { 144 | cell.m_obj_list[enter_.M_uid] = enter_ 145 | } 146 | if action_.PositionXY.Distance(¬ify_.PositionXY) < action_.M_view_range { 147 | 148 | cell.m_mgr_chan.Add(GoAoiAction{ 149 | GO_AOI_ACTION_UPDATE, 150 | action_.M_uid, 151 | notify_.M_uid, 152 | }) 153 | 154 | } else { 155 | 156 | cell.m_mgr_chan.Add(GoAoiAction{ 157 | GO_AOI_ACTION_QUIT, 158 | action_.M_uid, 159 | notify_.M_uid, 160 | }) 161 | 162 | } 163 | } 164 | for _, _it := range cell.m_obj_list { 165 | _func(enter_, _it) 166 | _func(_it, enter_) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /engine/YAoi/GoAoiManager.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 6 | ) 7 | 8 | type GoAoiMoveCallBack func(notify_, action_ uint64) 9 | type GoAoiEnterCallBack func(notify_, action_ uint64) 10 | type GoAoiQuitCallBack func(notify_, action_ uint64) 11 | 12 | const ( 13 | GO_AOI_ACTION_ENTER = iota 14 | GO_AOI_ACTION_UPDATE 15 | GO_AOI_ACTION_QUIT 16 | ) 17 | 18 | type GoAoiObj struct { 19 | M_uid uint64 20 | M_current_index uint64 21 | YTool.PositionXY 22 | M_view_range float64 23 | M_dirty bool 24 | } 25 | 26 | type GoAoiAction struct { 27 | m_action uint32 28 | m_notify_obj uint64 29 | m_action_obj uint64 30 | } 31 | 32 | type GoAoiManager struct { 33 | M_height float64 34 | M_width float64 35 | m_aoi_list map[uint32]*GoAoiCell 36 | M_current_index map[uint64]uint32 37 | m_block_height float64 38 | m_block_width float64 39 | m_block_size float64 40 | 41 | m_enter_callback GoAoiEnterCallBack 42 | m_update_callback GoAoiMoveCallBack 43 | m_quit_callback GoAoiQuitCallBack 44 | //m_action_list chan GoAoiAction 45 | m_action_list *YTool.SyncQueue 46 | } 47 | 48 | func NewGoAoiManager(width_, height_, block_size_ float64) *GoAoiManager { 49 | _mgr := &GoAoiManager{ 50 | m_aoi_list: make(map[uint32]*GoAoiCell), 51 | M_current_index: make(map[uint64]uint32), 52 | m_action_list: YTool.NewSyncQueue(), 53 | } 54 | _mgr.M_height = height_ 55 | _mgr.M_width = width_ 56 | _mgr.m_block_height = height_ / block_size_ 57 | _mgr.m_block_width = width_ / block_size_ 58 | _mgr.m_block_size = block_size_ 59 | return _mgr 60 | } 61 | 62 | func (mgr *GoAoiManager) Init(move_call_ GoAoiMoveCallBack, enter_call_ GoAoiEnterCallBack, quit_call_ GoAoiQuitCallBack) { 63 | 64 | mgr.m_enter_callback = enter_call_ 65 | mgr.m_update_callback = move_call_ 66 | mgr.m_quit_callback = quit_call_ 67 | 68 | for _row_idx := uint32(0); _row_idx < uint32(mgr.m_block_size); _row_idx++ { 69 | for _col_idx := uint32(0); _col_idx < uint32(mgr.m_block_size); _col_idx++ { 70 | _cell := NewGoAoiCell(mgr.m_action_list) 71 | mgr.m_aoi_list[mgr.buildIndex(_row_idx, _col_idx)] = _cell 72 | } 73 | } 74 | } 75 | 76 | func (mgr *GoAoiManager) Update() { 77 | for { 78 | if mgr.m_action_list.Len() == 0 { 79 | break 80 | } 81 | _act := mgr.m_action_list.Pop().(GoAoiAction) 82 | switch _act.m_action { 83 | case GO_AOI_ACTION_ENTER: 84 | mgr.m_enter_callback(_act.m_notify_obj, _act.m_action_obj) 85 | case GO_AOI_ACTION_UPDATE: 86 | mgr.m_update_callback(_act.m_notify_obj, _act.m_action_obj) 87 | case GO_AOI_ACTION_QUIT: 88 | mgr.m_quit_callback(_act.m_notify_obj, _act.m_action_obj) 89 | } 90 | } 91 | 92 | } 93 | 94 | func (mgr *GoAoiManager) Enter(enter_ GoAoiObj, pos_ Msg.PositionXY) { 95 | _current_index := mgr.CalcIndex(pos_) 96 | _cell := mgr.m_aoi_list[_current_index] 97 | _cell.EnterCell(enter_) 98 | _round_arr := mgr.getRoundBlock(_current_index) 99 | for _it := range _round_arr { 100 | _cell, exists := mgr.m_aoi_list[_it] 101 | if exists { 102 | _cell.NotifyEnterCell(enter_) 103 | } 104 | } 105 | mgr.M_current_index[enter_.M_uid] = _current_index 106 | } 107 | 108 | func (mgr *GoAoiManager) Quit(quit_ GoAoiObj, pos_ Msg.PositionXY) { 109 | _current_index := mgr.CalcIndex(pos_) 110 | _cell := mgr.m_aoi_list[_current_index] 111 | _cell.QuitCell(quit_) 112 | _round_arr := mgr.getRoundBlock(_current_index) 113 | for _it := range _round_arr { 114 | _cell, exists := mgr.m_aoi_list[_it] 115 | if exists { 116 | _cell.NotifyQuitCell(quit_) 117 | } 118 | } 119 | delete(mgr.M_current_index, quit_.M_uid) 120 | } 121 | 122 | func (mgr *GoAoiManager) Move(move_ GoAoiObj, pos_ Msg.PositionXY) { 123 | 124 | _old_round_arr := mgr.getOldRoundBlock(move_.M_uid) 125 | 126 | _current_index := mgr.CalcIndex(pos_) 127 | _new_round_arr := mgr.getRoundBlock(_current_index) 128 | 129 | if _current_index != mgr.M_current_index[move_.M_uid] { 130 | _enter_cell := mgr.m_aoi_list[_current_index] 131 | _enter_cell.EnterCell(move_) 132 | 133 | } 134 | _enter_cell := YTool.GetSetUint32Diff(_new_round_arr, _old_round_arr) 135 | for _it := range _enter_cell { 136 | _cell, exists := mgr.m_aoi_list[_it] 137 | if exists { 138 | _cell.NotifyEnterCell(move_) 139 | } 140 | } 141 | 142 | _update_cell := YTool.GetSetUint32Diff(_new_round_arr, _enter_cell) 143 | for _it := range _update_cell { 144 | _cell, exists := mgr.m_aoi_list[_it] 145 | if exists { 146 | _cell.UpdateCell(move_) 147 | } 148 | } 149 | if _current_index != mgr.M_current_index[move_.M_uid] { 150 | _quit_cell := mgr.m_aoi_list[mgr.M_current_index[move_.M_uid]] 151 | _quit_cell.QuitCell(move_) 152 | } 153 | _quit_cell := YTool.GetSetUint32Diff(_old_round_arr, _new_round_arr) 154 | for _it := range _quit_cell { 155 | _cell, exists := mgr.m_aoi_list[_it] 156 | if exists { 157 | _cell.NotifyQuitCell(move_) 158 | } 159 | } 160 | mgr.M_current_index[move_.M_uid] = _current_index 161 | 162 | } 163 | 164 | func (mgr *GoAoiManager) CalcIndex(xy_ Msg.PositionXY) uint32 { 165 | return mgr.buildIndex(uint32(xy_.M_x/mgr.m_block_width), uint32(xy_.M_y/mgr.m_block_height)) 166 | } 167 | 168 | func (mgr *GoAoiManager) buildIndex(row_, col_ uint32) uint32 { 169 | return row_ + col_*uint32(mgr.m_block_size) 170 | } 171 | 172 | func (mgr *GoAoiManager) getOldRoundBlock(uid_ uint64) map[uint32]struct{} { 173 | _old_index := mgr.M_current_index[uid_] 174 | return mgr.getRoundBlock(_old_index) 175 | } 176 | 177 | func (mgr *GoAoiManager) getRoundBlock(cent_index_ uint32) map[uint32]struct{} { 178 | _ret_round := make(map[uint32]struct{}) 179 | _cent_idex := int(cent_index_) 180 | _block_size := int(mgr.m_block_size) 181 | 182 | _max_idx := int(mgr.m_block_size * mgr.m_block_size) 183 | 184 | _cent_row := int(cent_index_ / uint32(mgr.m_block_size)) 185 | _ret_round[cent_index_] = struct{}{} 186 | { 187 | _left_up := _cent_idex - _block_size - 1 188 | if _left_up >= 0 && (_left_up/_block_size+1) == _cent_row { 189 | _ret_round[uint32(_left_up)] = struct{}{} 190 | } 191 | } 192 | 193 | { 194 | _up := _cent_idex - _block_size 195 | if _up >= 0 && (_up/_block_size+1) == _cent_row { 196 | _ret_round[uint32(_up)] = struct{}{} 197 | } 198 | } 199 | { 200 | _up_right := _cent_idex - _block_size + 1 201 | if _up_right >= 0 && (_up_right/_block_size+1) == _cent_row { 202 | _ret_round[uint32(_up_right)] = struct{}{} 203 | } 204 | } 205 | 206 | { 207 | _left := _cent_idex - 1 208 | if _left >= 0 && (_left/_block_size) == _cent_row { 209 | _ret_round[uint32(_left)] = struct{}{} 210 | } 211 | } 212 | { 213 | _right := _cent_idex + 1 214 | if _right >= 0 && (_right/_block_size) == _cent_row { 215 | _ret_round[uint32(_right)] = struct{}{} 216 | } 217 | } 218 | 219 | { 220 | _down_left := _cent_idex + _block_size - 1 221 | if _down_left < _max_idx && (_down_left/_block_size-1) == _cent_row { 222 | _ret_round[uint32(_down_left)] = struct{}{} 223 | } 224 | } 225 | 226 | { 227 | _down := _cent_idex + _block_size 228 | if _down < _max_idx && (_down/_block_size-1) == _cent_row { 229 | _ret_round[uint32(_down)] = struct{}{} 230 | } 231 | } 232 | { 233 | _down_right := _cent_idex + _block_size + 1 234 | if _down_right < _max_idx && (_down_right/_block_size-1) == _cent_row { 235 | _ret_round[uint32(_down_right)] = struct{}{} 236 | } 237 | } 238 | 239 | return _ret_round 240 | } 241 | -------------------------------------------------------------------------------- /engine/YAoi/GoNineGridAoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | type GoNineGirdAoiCell struct { 4 | m_watch_list map[uint64]struct{} 5 | } 6 | 7 | func NewGoNineGirdAoiCell() *GoNineGirdAoiCell { 8 | _cell := &GoNineGirdAoiCell{ 9 | m_watch_list: make(map[uint64]struct{}), 10 | } 11 | return _cell 12 | } 13 | func (cell *GoNineGirdAoiCell)GetWatch()map[uint64]struct{}{ 14 | return cell.m_watch_list 15 | } 16 | func (cell *GoNineGirdAoiCell)Watch(uid_ uint64){ 17 | cell.m_watch_list[uid_] = struct{}{} 18 | } 19 | 20 | func (cell *GoNineGirdAoiCell)Forget(uid_ uint64){ 21 | delete(cell.m_watch_list, uid_) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /engine/YAoi/GoTowerAoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import "github.com/yxinyi/YCServer/engine/YTool" 4 | 5 | type GoTowerAoiObj struct { 6 | M_uid uint64 7 | M_current_index uint64 8 | *YTool.PositionXY 9 | M_view_range float64 10 | M_dirty bool 11 | M_watch_list map[uint64]struct{} //当前关注哪些对象 12 | M_watch_tower_list map[uint64]struct{} //当前关注哪些塔 13 | } 14 | 15 | func (obj *GoTowerAoiObj) InViewRange(rhs_ *GoTowerAoiObj) bool { 16 | return obj.PositionXY.Distance(rhs_.PositionXY) < obj.M_view_range 17 | } 18 | func NewGoTowerAoiObj() *GoTowerAoiObj { 19 | _obj := &GoTowerAoiObj{ 20 | M_watch_list: make(map[uint64]struct{}), 21 | M_watch_tower_list: make(map[uint64]struct{}), 22 | } 23 | return _obj 24 | } 25 | 26 | type AoiTower struct { 27 | m_index uint64 28 | m_obj_list map[uint64]struct{} //当前灯塔范围内有多少人 29 | m_watch_this_obj map[uint64]struct{} //当前有多少人监控该灯塔,也就是需要将该灯塔的视野内信息进行同步 30 | m_position *YTool.PositionXY 31 | m_view_range float64 //当玩家进入这个范围后就算成当前灯塔内的玩家 32 | } 33 | 34 | func NewGoTowerAoiCell() *AoiTower { 35 | _cell := &AoiTower{ 36 | m_obj_list: make(map[uint64]struct{}), 37 | m_watch_this_obj: make(map[uint64]struct{}), 38 | } 39 | return _cell 40 | } 41 | func (tower *AoiTower) GetWatch() map[uint64]struct{} { 42 | return tower.m_watch_this_obj 43 | } 44 | func (tower *AoiTower) AddWatch(uid_ uint64) { 45 | tower.m_watch_this_obj[uid_] = struct{}{} 46 | } 47 | 48 | func (tower *AoiTower) RemoveWatch(uid_ uint64) { 49 | delete(tower.m_watch_this_obj, uid_) 50 | } 51 | 52 | func (tower *AoiTower) GetObjs() map[uint64]struct{} { 53 | return tower.m_obj_list 54 | } 55 | 56 | func (tower *AoiTower) Add(uid_ uint64) bool { 57 | _, exists := tower.m_obj_list[uid_] 58 | if exists { 59 | return false 60 | } 61 | tower.m_obj_list[uid_] = struct{}{} 62 | return true 63 | } 64 | 65 | func (tower *AoiTower) Remove(uid_ uint64) bool { 66 | _, exists := tower.m_obj_list[uid_] 67 | if !exists { 68 | return false 69 | } 70 | delete(tower.m_obj_list, uid_) 71 | return true 72 | } 73 | -------------------------------------------------------------------------------- /engine/YAttr/Attribute.go: -------------------------------------------------------------------------------- 1 | package YAttr 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type AttributeValue struct { 9 | M_entity_name string 10 | M_attr_name string 11 | M_value_stream []byte 12 | M_value reflect.Value 13 | M_value_steam_convert bool 14 | } 15 | 16 | 17 | func (av *AttributeValue) GetTemplate() *Template { 18 | return nil 19 | } 20 | 21 | func (av *AttributeValue) GetDebugString() string { 22 | _ret_str := "" 23 | _ret_str += fmt.Sprintf("[name:%v][value:%v]", av.M_attr_name,av.M_value.String()) 24 | return _ret_str 25 | } 26 | 27 | type AttributeValuePanel struct { 28 | M_name string 29 | M_attr_list map[string]*AttributeValue 30 | } 31 | 32 | func NewAttributeValuePanel()*AttributeValuePanel{ 33 | _panel := &AttributeValuePanel{} 34 | _panel.M_attr_list = make(map[string]*AttributeValue) 35 | return _panel 36 | } 37 | 38 | func (p *AttributeValuePanel) GetAttr(route_ string) interface{} { 39 | _attr, _exists := p.M_attr_list[route_] 40 | if !_exists { 41 | return nil 42 | } 43 | if _attr.M_value.CanAddr(){ 44 | return _attr.M_value.Addr().Interface() 45 | } 46 | return _attr.M_value.Interface() 47 | } 48 | -------------------------------------------------------------------------------- /engine/YAttr/AttributeTemplate.go: -------------------------------------------------------------------------------- 1 | package YAttr 2 | 3 | import ( 4 | jsoniter "github.com/json-iterator/go" 5 | "reflect" 6 | ) 7 | 8 | type Template struct { 9 | M_entity_name string 10 | M_attr_name string 11 | M_attr_type_ref reflect.Type 12 | M_defalut_value reflect.Value // 13 | M_save bool 14 | M_sync_to_ghost bool 15 | M_sync_to_self_client bool 16 | M_sync_to_other_client bool 17 | } 18 | 19 | func Tmpl(name_ string, attr_type_ interface{}, to_save_, to_ghost, to_self_cli, to_other_cli bool) *Template { 20 | _tmpl := &Template{} 21 | _attr_ref_val := reflect.ValueOf(attr_type_) 22 | _tmpl.M_entity_name = name_ 23 | _tmpl.M_attr_type_ref = _attr_ref_val.Type() 24 | _tmpl.M_defalut_value = _attr_ref_val 25 | _tmpl.M_save = to_save_ 26 | _tmpl.M_sync_to_ghost = to_ghost 27 | _tmpl.M_sync_to_self_client = to_self_cli 28 | _tmpl.M_sync_to_other_client = to_other_cli 29 | return _tmpl 30 | } 31 | func (tmpl *Template) New() *AttributeValue { 32 | _attr_val := &AttributeValue{} 33 | _attr_val.M_entity_name = tmpl.M_entity_name 34 | _attr_val.M_attr_name = tmpl.M_attr_name 35 | 36 | _tmp_new_val := reflect.New(tmpl.M_defalut_value.Type()).Elem() 37 | 38 | _tmp_new_val.Set(tmpl.M_defalut_value) 39 | _attr_val.M_value = _tmp_new_val 40 | 41 | _bytes, _err := jsoniter.Marshal(_attr_val.M_value.Interface()) 42 | _attr_val.M_value_stream = _bytes 43 | if _err != nil { 44 | panic(_err.Error()) 45 | } 46 | return _attr_val 47 | } 48 | 49 | type TemplatePanel struct { 50 | M_entity_name string 51 | M_attr_tmpl_list map[string]*Template 52 | } 53 | 54 | func NewTemplatePanel() *TemplatePanel { 55 | _panel := &TemplatePanel{} 56 | _panel.M_attr_tmpl_list = make(map[string]*Template) 57 | return _panel 58 | } 59 | 60 | func Define(name_ string, attr_list_ ...*Template) *TemplatePanel { 61 | _panel := NewTemplatePanel() 62 | for _, _it := range attr_list_ { 63 | _key := name_ + "." + _it.M_entity_name 64 | _it.M_attr_name = _key 65 | _panel.M_attr_tmpl_list[_key] = _it 66 | } 67 | 68 | return _panel 69 | } 70 | 71 | func (panel *TemplatePanel) New() *AttributeValuePanel { 72 | _value_panel := NewAttributeValuePanel() 73 | _value_panel.M_name = panel.M_entity_name 74 | for _, _tmpl_it := range panel.M_attr_tmpl_list { 75 | _value_panel.M_attr_list[_tmpl_it.M_attr_name] = _tmpl_it.New() 76 | } 77 | return _value_panel 78 | } 79 | 80 | type AttrTmplPanelManager struct { 81 | m_tmpl_panel_list map[string]*TemplatePanel 82 | } 83 | 84 | func NewAttrTmplPanelManager() *AttrTmplPanelManager { 85 | _mgr := &AttrTmplPanelManager{ 86 | m_tmpl_panel_list: make(map[string]*TemplatePanel), 87 | } 88 | return _mgr 89 | } 90 | 91 | func (mgr *AttrTmplPanelManager) RegisterEntityAttr(entity_name_ string, panel *TemplatePanel) { 92 | _, _exists := mgr.m_tmpl_panel_list[entity_name_] 93 | if !_exists { 94 | mgr.m_tmpl_panel_list[entity_name_] = &TemplatePanel{ 95 | entity_name_, 96 | make(map[string]*Template), 97 | } 98 | } 99 | for _, _attr_it := range panel.M_attr_tmpl_list { 100 | _attr_it.M_entity_name = entity_name_ 101 | mgr.m_tmpl_panel_list[entity_name_].M_attr_tmpl_list[_attr_it.M_attr_name] = _attr_it 102 | } 103 | 104 | } 105 | 106 | func (mgr *AttrTmplPanelManager) New(type_str_ string) *AttributeValuePanel { 107 | _prototype, _exists := mgr.m_tmpl_panel_list[type_str_] 108 | if !_exists { 109 | return nil 110 | } 111 | return _prototype.New() 112 | } 113 | -------------------------------------------------------------------------------- /engine/YConfig/Config.go: -------------------------------------------------------------------------------- 1 | package YConfig 2 | 3 | import "github.com/spf13/viper" 4 | 5 | const DEFAULT_PATH = "./config" 6 | 7 | 8 | func Load(name_ string, core_ interface{}) error { 9 | _viper := viper.New() 10 | _viper.SetConfigName(name_) // name of config file (without extension) 11 | _viper.SetConfigType("json") // REQUIRED if the config file does not have the extension in the name 12 | _viper.AddConfigPath(DEFAULT_PATH) // path to look for the config file in 13 | _viper.ReadInConfig() 14 | return _viper.Unmarshal(core_) 15 | } 16 | -------------------------------------------------------------------------------- /engine/YConsul/Consul.md: -------------------------------------------------------------------------------- 1 | ## 启动 2 | 3 | ``` 4 | consul agent -dev 5 | ``` 6 | 7 | ## 查看当前运行中的consul 8 | 9 | ``` 10 | consul members 11 | ``` 12 | 13 | ## 关闭 14 | 15 | ``` 16 | consul leave 17 | ``` 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /engine/YDecode/Decode.go: -------------------------------------------------------------------------------- 1 | package YDecode 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | ) 6 | 7 | const ( 8 | DECODE_TYPE_JSON uint32 = 0 9 | ) 10 | 11 | type Inter interface { 12 | Marshal(interface{}) ([]byte, error) 13 | Unmarshal([]byte, interface{}) error 14 | } 15 | 16 | var g_decode_pool = make(map[uint32]Inter) 17 | 18 | func init() { 19 | g_decode_pool[DECODE_TYPE_JSON] = &Json{} 20 | } 21 | 22 | type Json struct{} 23 | 24 | func (j *Json) Marshal(val interface{}) ([]byte, error) { 25 | return jsoniter.Marshal(val) 26 | } 27 | func (j *Json) Unmarshal(data_ []byte, val_ interface{}) error { 28 | return jsoniter.Unmarshal(data_, val_) 29 | } 30 | 31 | func Marshal(type_ uint32,val interface{} )([]byte, error){ 32 | return g_decode_pool[type_].Marshal(val) 33 | } 34 | 35 | func Unmarshal(type_ uint32,data_ []byte, val_ interface{})error{ 36 | return g_decode_pool[type_].Unmarshal(data_,val_) 37 | } -------------------------------------------------------------------------------- /engine/YEntity/Entity.go: -------------------------------------------------------------------------------- 1 | package YEntity 2 | 3 | import "github.com/yxinyi/YCServer/engine/YAttr" 4 | 5 | var g_attr_tmpl_mgr = YAttr.NewAttrTmplPanelManager() 6 | 7 | func RegisterEntityAttr(entity_name_ string, panel_list_ ...*YAttr.TemplatePanel) { 8 | for _, _panel_it := range panel_list_ { 9 | g_attr_tmpl_mgr.RegisterEntityAttr(entity_name_, _panel_it) 10 | } 11 | } 12 | 13 | func New(type_str_ string) *Info { 14 | _info := NewInfo() 15 | _attr := g_attr_tmpl_mgr.New(type_str_) 16 | if _attr == nil { 17 | return nil 18 | } 19 | _info.M_entity_type = type_str_ 20 | _info.AttributeValuePanel = _attr 21 | return _info 22 | } 23 | 24 | func NewWithUID(type_str_ string, entity_uid_ uint64) *Info { 25 | _info := New(type_str_) 26 | if _info == nil { 27 | return nil 28 | } 29 | _info.M_uid = entity_uid_ 30 | return _info 31 | } 32 | -------------------------------------------------------------------------------- /engine/YEntity/Info.go: -------------------------------------------------------------------------------- 1 | package YEntity 2 | 3 | import ( 4 | "fmt" 5 | attr "github.com/yxinyi/YCServer/engine/YAttr" 6 | ) 7 | 8 | type Info struct { 9 | M_uid uint64 10 | M_entity_type string 11 | M_is_ghost bool 12 | *attr.AttributeValuePanel 13 | } 14 | 15 | func NewInfo() *Info { 16 | _info := &Info{} 17 | return _info 18 | } 19 | 20 | func (e *Info) GetInfo() *Info { 21 | return e 22 | } 23 | 24 | func (e *Info) GetDebugString() string { 25 | _ret_str := "" 26 | _ret_str += fmt.Sprintf("[UID:%v][EntityType:%v][IsGhost:%v]\n",e.M_uid,e.M_entity_type,e.M_is_ghost) 27 | for _,_attr_it := range e.M_attr_list{ 28 | _ret_str+= _attr_it.GetDebugString() 29 | } 30 | 31 | return _ret_str 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /engine/YEntity/entity_test.go: -------------------------------------------------------------------------------- 1 | package YEntity 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YAttr" 5 | "testing" 6 | ) 7 | 8 | type TmpStruct struct { 9 | M_val int 10 | } 11 | 12 | func TestEntity(t *testing.T) { 13 | RegisterEntityAttr("Test", 14 | YAttr.Define("AttrPanel_1", 15 | YAttr.Tmpl("attr_1", TmpStruct{M_val: 9}, true, true, true, true), 16 | ), 17 | YAttr.Define("AttrPanel_2", 18 | YAttr.Tmpl("attr_1", []uint32{1,2,3}, true, true, true, true), 19 | YAttr.Tmpl("attr_2", uint32(2), true, true, true, true), 20 | YAttr.Tmpl("attr_3", uint32(3), true, true, true, true), 21 | YAttr.Tmpl("attr_4", uint32(4), true, true, true, true), 22 | YAttr.Tmpl("attr_5", uint32(5), true, true, true, true), 23 | )) 24 | _entity_1 := NewWithUID("Test", 1) 25 | { 26 | _tmp, _err := _entity_1.GetAttr("AttrPanel_1.attr_1").(*TmpStruct) 27 | if !_err { 28 | t.Errorf("[%v]", _err) 29 | return 30 | } 31 | t.Logf("[%v]", _tmp.M_val) 32 | _tmp.M_val = 100 33 | } 34 | { 35 | _tmp, _ := _entity_1.GetAttr("AttrPanel_1.attr_1").(*TmpStruct) 36 | t.Logf("[%v]", _tmp.M_val) 37 | } 38 | { 39 | _tmp, _ := _entity_1.GetAttr("AttrPanel_2.attr_1").(*[]uint32) 40 | t.Logf("[%v]", *_tmp) 41 | } 42 | { 43 | _tmp, _ := _entity_1.GetAttr("AttrPanel_2.attr_2").(*uint32) 44 | t.Logf("[%v]", *_tmp) 45 | *_tmp = 1000 46 | } 47 | { 48 | _tmp, _ := _entity_1.GetAttr("AttrPanel_2.attr_3").(*uint32) 49 | t.Logf("[%v]", *_tmp) 50 | } 51 | { 52 | _tmp, _ := _entity_1.GetAttr("AttrPanel_2.attr_4").(*uint32) 53 | t.Logf("[%v]", *_tmp) 54 | } 55 | { 56 | _tmp, _ := _entity_1.GetAttr("AttrPanel_2.attr_5").(*uint32) 57 | t.Logf("[%v]", *_tmp) 58 | } 59 | { 60 | _tmp, _ := _entity_1.GetAttr("AttrPanel_2.attr_2").(*uint32) 61 | t.Logf("[%v]", *_tmp) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /engine/YJson/json.go: -------------------------------------------------------------------------------- 1 | package YJson 2 | 3 | import ( 4 | "fmt" 5 | jsoniter "github.com/json-iterator/go" 6 | ) 7 | 8 | var g_ghost_json = jsoniter.Config{TagKey: "SG", OnlyTaggedField: true}.Froze() 9 | var g_save_json = jsoniter.Config{TagKey: "SA", OnlyTaggedField: true}.Froze() 10 | var g_sync_self_json = jsoniter.Config{TagKey: "SS", OnlyTaggedField: true}.Froze() 11 | var g_sync_other_json = jsoniter.Config{TagKey: "SO", OnlyTaggedField: true}.Froze() 12 | 13 | var g_print_json = jsoniter.Config{SortMapKeys: true, MarshalFloatWith6Digits: true, IndentionStep: 4}.Froze() 14 | 15 | func GetPrintStr(val interface{}) string { 16 | _print_str, _err := g_print_json.MarshalToString(val) 17 | if _err != nil { 18 | return fmt.Sprintf("Marshal err [%v]", _err.Error()) 19 | } 20 | return _print_str 21 | } 22 | 23 | func UnMarshal(str_ string,val_ interface{}) (error) { 24 | return jsoniter.UnmarshalFromString(str_,val_) 25 | } 26 | 27 | func GhostMarshal(val_ interface{}) (string, error) { 28 | return g_ghost_json.MarshalToString(val_) 29 | } 30 | 31 | 32 | 33 | func SaveMarshal(val_ interface{}) (string, error) { 34 | return g_save_json.MarshalToString(val_) 35 | } 36 | func SyncOtherMarshal(val_ interface{}) (string, error) { 37 | return g_sync_other_json.MarshalToString(val_) 38 | } 39 | func SyncSelfMarshal(val_ interface{}) (string, error) { 40 | return g_sync_self_json.MarshalToString(val_) 41 | } 42 | -------------------------------------------------------------------------------- /engine/YLog/Log.go: -------------------------------------------------------------------------------- 1 | package ylog 2 | 3 | import ( 4 | "fmt" 5 | "github.com/lestrrat/go-file-rotatelogs" 6 | "go.uber.org/zap" 7 | "go.uber.org/zap/zapcore" 8 | "io" 9 | "os" 10 | "time" 11 | ) 12 | 13 | var Logger *zap.SugaredLogger 14 | 15 | var Erro func(template string, args ...interface{}) 16 | var Info func(template string, args ...interface{}) 17 | var Warn func(template string, args ...interface{}) 18 | 19 | 20 | 21 | /*func Erro(template string, args ...interface{}){ 22 | Logger.Errorf(template,args...) 23 | } 24 | func Info(template string, args ...interface{}){ 25 | Logger.Infof(template,args...) 26 | } 27 | func Warn(template string, args ...interface{}){ 28 | Logger.Warnf(template,args...) 29 | }*/ 30 | func init() { 31 | initLogger() 32 | 33 | } 34 | func initLogger() { 35 | logPath := "./server" 36 | if !exists(logPath) { 37 | file, err := os.Create(logPath) 38 | defer file.Close() 39 | if err != nil { 40 | fmt.Println("mkdir logPath err! [%v]", err.Error()) 41 | return 42 | } 43 | } 44 | 45 | encoder := initEncoder() 46 | 47 | // 想要将日常文件区分开来,可以实现多个日志等级接口 48 | /*infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 49 | return lvl < zapcore.WarnLevel 50 | })*/ 51 | debugLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { 52 | return lvl >= zapcore.DebugLevel 53 | }) 54 | 55 | // 获取 info、warn日志文件的io.Writer 56 | warnIoWriter := getWriter(logPath) 57 | 58 | // 创建Logger 59 | core := zapcore.NewTee( 60 | zapcore.NewCore(encoder, zapcore.AddSync(warnIoWriter), debugLevel), 61 | zapcore.NewCore(encoder, zapcore.AddSync(os.Stdout), debugLevel), 62 | ) 63 | logger := zap.New(core, zap.AddCaller()) // 需要传入 zap.AddCaller() 才会显示打日志点的文件名和行数 64 | Logger = logger.Sugar() 65 | 66 | Erro = Logger.Errorf 67 | Info = Logger.Infof 68 | Warn = Logger.Warnf 69 | 70 | } 71 | 72 | //初始化Encoder 73 | func initEncoder() zapcore.Encoder { 74 | return zapcore.NewConsoleEncoder(zapcore.EncoderConfig{ 75 | MessageKey: "msg", 76 | LevelKey: "level", 77 | TimeKey: "time", 78 | CallerKey: "file", 79 | EncodeLevel: zapcore.CapitalLevelEncoder, //基本zapcore.LowercaseLevelEncoder。将日志级别字符串转化为小写 80 | EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 81 | enc.AppendString(t.Format("2006-01-02 15:04:05")) 82 | }, 83 | EncodeCaller: zapcore.ShortCallerEncoder, //一般zapcore.ShortCallerEncoder,以包/文件:行号 格式化调用堆栈 84 | EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { //一般zapcore.SecondsDurationEncoder,执行消耗的时间转化成浮点型的秒 85 | enc.AppendInt64(int64(d) / 1000000) 86 | }, 87 | }) 88 | } 89 | 90 | //日志文件切割 91 | func getWriter(filename string) io.Writer { 92 | // 保存30天内的日志,每24小时(整点)分割一次日志 93 | hook, err := rotatelogs.New( 94 | filename+".%M_y%m%d", 95 | rotatelogs.WithLinkName(filename), 96 | rotatelogs.WithMaxAge(time.Hour*24*30), 97 | rotatelogs.WithRotationTime(time.Hour*24), 98 | ) 99 | 100 | if err != nil { 101 | panic(err) 102 | } 103 | return hook 104 | } 105 | 106 | //查看文件/文件夹是否存在 107 | func exists(path string) bool { 108 | _, err := os.Stat(path) 109 | if err != nil { 110 | if os.IsExist(err) { 111 | return true 112 | } 113 | return false 114 | } 115 | return true 116 | } 117 | -------------------------------------------------------------------------------- /engine/YModule/Info.go: -------------------------------------------------------------------------------- 1 | package YModule 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YDecode" 5 | "github.com/yxinyi/YCServer/engine/YEntity" 6 | ylog "github.com/yxinyi/YCServer/engine/YLog" 7 | "github.com/yxinyi/YCServer/engine/YMsg" 8 | "github.com/yxinyi/YCServer/engine/YTool" 9 | "reflect" 10 | "time" 11 | ) 12 | 13 | type Inter interface { 14 | GetInfo() *Info 15 | Init() 16 | Loop_1(time time.Time) 17 | Loop_10(time time.Time) 18 | Loop_100(time time.Time) 19 | Close() 20 | } 21 | 22 | type BaseInter struct { 23 | *Info 24 | } 25 | 26 | func (b *BaseInter) GetInfo() *Info { return b.Info } 27 | func (b *BaseInter) Loop_1(time time.Time) {} 28 | func (b *BaseInter) Loop_10(time time.Time) {} 29 | func (b *BaseInter) Loop_100(time time.Time) {} 30 | func (b *BaseInter) Init() {} 31 | func (b *BaseInter) Close() {} 32 | 33 | type RPCFunc struct { 34 | M_rpc_name string 35 | M_fn reflect.Value 36 | M_param []reflect.Type 37 | M_back_param []reflect.Type 38 | } 39 | 40 | type NetFunc struct { 41 | M_net_name string 42 | M_fn reflect.Value 43 | m_msg_data reflect.Type 44 | } 45 | 46 | type RemoteNodeER interface { 47 | RPCToOther(msg *YMsg.S2S_rpc_msg) 48 | NetToOther(msg *YMsg.C2S_net_msg) 49 | } 50 | 51 | type Info struct { 52 | RemoteNodeER 53 | M_agent YMsg.Agent 54 | /*M_name string 55 | M_module_uid uint64*/ 56 | 57 | M_entity_pool map[uint64]YEntity.Info 58 | M_rpc_queue *YTool.SyncQueue 59 | M_net_queue *YTool.SyncQueue 60 | 61 | M_rpc_func_map map[string]*RPCFunc 62 | M_net_func_map map[string]*NetFunc 63 | m_cb_list_cancel bool 64 | M_back_fun map[uint64]*RPCCommandList 65 | } 66 | 67 | type RPCCommand struct { 68 | M_uid uint64 69 | M_tar_agent YMsg.Agent 70 | M_tar_rpc_func_name string 71 | M_tar_rpc_param_list []interface{} 72 | M_need_back bool 73 | M_back_func reflect.Value 74 | M_back_param []reflect.Type 75 | } 76 | 77 | func NewRPCMsg(tar_agent_ YMsg.Agent, tar_func_name_ string, param_list_ ...interface{}) *YMsg.S2S_rpc_msg { 78 | return NewRPCCommand(tar_agent_, tar_func_name_, param_list_...).ToRPCMsg() 79 | } 80 | 81 | 82 | 83 | func NewRPCCommand(tar_agent_ YMsg.Agent, tar_func_name_ string, param_list_ ...interface{}) *RPCCommand { 84 | _rpc_cmd := &RPCCommand{} 85 | _rpc_cmd.M_uid = YTool.BuildUIDUint64() 86 | _rpc_cmd.M_tar_agent = tar_agent_ 87 | _rpc_cmd.M_tar_rpc_func_name = tar_func_name_ 88 | 89 | if len(param_list_) > 0 { 90 | _cb_func_value := reflect.ValueOf(param_list_[len(param_list_)-1]) 91 | if _cb_func_value.Type().Kind() == reflect.Func { 92 | _rpc_cmd.M_need_back = true 93 | _rpc_cmd.M_back_func = _cb_func_value 94 | _rpc_cmd.M_back_param = YTool.GetFuncInTypeList(_cb_func_value) 95 | _rpc_cmd.M_tar_rpc_param_list = param_list_[:len(param_list_)-1] 96 | } else { 97 | _rpc_cmd.M_tar_rpc_param_list = param_list_ 98 | } 99 | } 100 | 101 | return _rpc_cmd 102 | } 103 | 104 | type RPCCommandList struct { 105 | M_uid uint64 106 | M_cur_idx uint32 107 | m_command_list []*RPCCommand 108 | } 109 | 110 | func NewRPCCommandList() *RPCCommandList { 111 | _cmd_list := &RPCCommandList{} 112 | _cmd_list.M_uid = YTool.BuildUIDUint64() 113 | _cmd_list.m_command_list = make([]*RPCCommand, 0) 114 | return _cmd_list 115 | } 116 | 117 | func NewInfo(node_ RemoteNodeER) *Info { 118 | _info := &Info{ 119 | M_entity_pool: make(map[uint64]YEntity.Info), 120 | M_rpc_func_map: make(map[string]*RPCFunc), 121 | M_net_func_map: make(map[string]*NetFunc), 122 | M_rpc_queue: YTool.NewSyncQueue(), 123 | M_net_queue: YTool.NewSyncQueue(), 124 | M_back_fun: make(map[uint64]*RPCCommandList), 125 | RemoteNodeER: node_, 126 | } 127 | return _info 128 | } 129 | 130 | func (cmd *RPCCommand) ToRPCMsg() *YMsg.S2S_rpc_msg { 131 | _rpc_msg := &YMsg.S2S_rpc_msg{ 132 | M_uid: cmd.M_uid, 133 | M_tar: cmd.M_tar_agent, 134 | M_marshal_type: YDecode.DECODE_TYPE_JSON, 135 | M_func_name: cmd.M_tar_rpc_func_name, 136 | } 137 | if len(cmd.M_tar_rpc_param_list) > 0 { 138 | _rpc_msg.M_func_parameter = make([][]byte, 0, len(cmd.M_tar_rpc_param_list)) 139 | for _, _param_it := range cmd.M_tar_rpc_param_list { 140 | _param_byte, _err := YDecode.Marshal(_rpc_msg.M_marshal_type, _param_it) 141 | if _err != nil { 142 | ylog.Erro("[RPCToOther] tar [%v:%v] [%v]", cmd.M_tar_agent.DebugString(), _err.Error()) 143 | return nil 144 | } 145 | _rpc_msg.M_func_parameter = append(_rpc_msg.M_func_parameter, _param_byte) 146 | } 147 | } 148 | return _rpc_msg 149 | } 150 | 151 | func (list *RPCCommandList) AppendCmdObj(cmd *RPCCommand) *RPCCommandList { 152 | cmd.M_uid = list.M_uid 153 | if len(list.m_command_list) == 0 { 154 | cmd.M_need_back = true 155 | } 156 | list.m_command_list = append(list.m_command_list, cmd) 157 | return list 158 | } 159 | 160 | func (list *RPCCommandList) AfterRPC(tar_agent_ YMsg.Agent, func_ string, param_list_ ...interface{}) *RPCCommandList { 161 | return list.AppendCmdObj(NewRPCCommand(tar_agent_, func_, param_list_...)) 162 | } 163 | 164 | func (list *RPCCommandList) getCurCmd() *RPCCommand { 165 | return list.m_command_list[list.M_cur_idx] 166 | } 167 | 168 | func (list *RPCCommandList) call(param_val_ []reflect.Value) { 169 | if list.getCurCmd().M_back_func.IsValid() { 170 | list.getCurCmd().M_back_func.Call(param_val_) 171 | } 172 | 173 | list.M_cur_idx++ 174 | } 175 | func (list *RPCCommandList) isOver() bool { 176 | return list.M_cur_idx >= uint32(len(list.m_command_list)) 177 | } 178 | 179 | func (list *RPCCommandList) popMsg() *YMsg.S2S_rpc_msg { 180 | _cur_cmd := list.getCurCmd() 181 | _rpc_msg := _cur_cmd.ToRPCMsg() 182 | _rpc_msg.M_need_back = true 183 | if list.isOver() { 184 | if !_cur_cmd.M_back_func.IsValid() { 185 | _rpc_msg.M_need_back = false 186 | } 187 | } 188 | 189 | return _rpc_msg 190 | } 191 | -------------------------------------------------------------------------------- /engine/YModule/Module.go: -------------------------------------------------------------------------------- 1 | package YModule 2 | 3 | import ( 4 | "fmt" 5 | "github.com/json-iterator/go" 6 | ylog "github.com/yxinyi/YCServer/engine/YLog" 7 | "github.com/yxinyi/YCServer/engine/YMsg" 8 | "github.com/yxinyi/YCServer/engine/YNet" 9 | "reflect" 10 | "runtime/debug" 11 | "strings" 12 | ) 13 | 14 | func (i *Info) PushRpcMsg(msg_ *YMsg.S2S_rpc_msg) { 15 | i.M_rpc_queue.Add(msg_) 16 | } 17 | func (i *Info) PushNetMsg(msg_ *YMsg.C2S_net_msg) { 18 | i.M_net_queue.Add(msg_) 19 | } 20 | 21 | func (i *Info) buildRPCFunc(func_name_ string, method_val_ reflect.Value) *RPCFunc { 22 | _func := &RPCFunc{ 23 | M_rpc_name: func_name_, 24 | M_fn: method_val_, 25 | } 26 | if method_val_.Type().NumIn() > 0 { 27 | _func.M_param = make([]reflect.Type, 0) 28 | for _in_idx := 0; _in_idx < method_val_.Type().NumIn(); _in_idx++ { 29 | _func.M_param = append(_func.M_param, method_val_.Type().In(_in_idx)) 30 | } 31 | } 32 | if method_val_.Type().NumOut() > 0 { 33 | _func.M_back_param = make([]reflect.Type, 0) 34 | for _out_idx := 0; _out_idx < method_val_.Type().NumOut(); _out_idx++ { 35 | _func.M_back_param = append(_func.M_back_param, method_val_.Type().Out(_out_idx)) 36 | } 37 | } 38 | return _func 39 | } 40 | 41 | func (i *Info) buildNetFunc(func_name_ string, method_val_ reflect.Value) *NetFunc { 42 | if method_val_.Type().NumIn() != 2 { 43 | return nil 44 | } 45 | _func := &NetFunc{ 46 | M_net_name: func_name_, 47 | M_fn: method_val_, 48 | } 49 | _func.m_msg_data = method_val_.Type().In(1) 50 | return _func 51 | } 52 | 53 | func (i *Info) CancelCBList() { 54 | i.m_cb_list_cancel = true 55 | } 56 | 57 | func (i *Info) Init(core Inter) { 58 | _ref_val := reflect.ValueOf(core) 59 | _method_num := _ref_val.NumMethod() 60 | for idx := 0; idx < _method_num; idx++ { 61 | _method_val := _ref_val.Method(idx) 62 | _func_name := _ref_val.Type().Method(idx).Name 63 | { 64 | _prefix_str := "RPC_" 65 | _str_idx := strings.Index(_func_name, _prefix_str) 66 | if _str_idx != -1 { 67 | _func := i.buildRPCFunc(_func_name, _method_val) 68 | i.M_rpc_func_map[_func.M_rpc_name[_str_idx+len(_prefix_str):]] = _func 69 | } 70 | } 71 | { 72 | _prefix_str := "MSG_" 73 | _str_idx := strings.Index(_func_name, _prefix_str) 74 | if _str_idx != -1 { 75 | _func := i.buildNetFunc(_func_name, _method_val) 76 | i.M_net_func_map[_func.M_net_name[_str_idx+len(_prefix_str):]] = _func 77 | } 78 | } 79 | } 80 | if len(i.M_net_func_map) > 0 { 81 | _msg_name_list := make([]string, 0) 82 | for _k_it := range i.M_net_func_map { 83 | _msg_name_list = append(_msg_name_list, _k_it) 84 | } 85 | i.RPCCall(YMsg.ToAgent("NetModule"), "NetMsgRegister", _msg_name_list, i.GetAgent()) 86 | } 87 | } 88 | 89 | func (i *Info) DebugPrint() { 90 | for _name_it, _func_it := range i.M_rpc_func_map { 91 | ylog.Info("#############") 92 | ylog.Info("[name] [%v] [func] [%v]", _name_it, _func_it.M_fn.String()) 93 | for _, _param_it := range _func_it.M_param { 94 | ylog.Info("param [%v]", _param_it.String()) 95 | } 96 | } 97 | } 98 | func (i *Info) paramUnmarshalWithTypeSlice(bytes_list_ [][]byte, type_list_ []reflect.Type) []reflect.Value { 99 | _param_list := make([]reflect.Value, 0) 100 | for _idx := 0; _idx < len(type_list_); _idx++ { 101 | _param_val := reflect.New(type_list_[_idx]).Interface() 102 | err := jsoniter.Unmarshal(bytes_list_[_idx], _param_val) 103 | if err != nil { 104 | ylog.Erro("RPC unmarshal err [%v] ", err.Error()) 105 | return nil 106 | } 107 | _param_list = append(_param_list, reflect.ValueOf(_param_val).Elem()) 108 | } 109 | return _param_list 110 | } 111 | 112 | func (i *Info) msgUnmarshalParamList(msg *YMsg.S2S_rpc_msg) []reflect.Value { 113 | _func, _exists := i.M_rpc_func_map[msg.M_func_name] 114 | if !_exists { 115 | ylog.Erro("[%v] RPC param miss method [%v]", msg.M_tar.M_module_name, msg.M_func_name) 116 | return nil 117 | } 118 | 119 | if len(_func.M_param) != len(msg.M_func_parameter) { 120 | ylog.Erro("[%v]RPC [%v]param count err right [%v] err [%v]", msg.M_tar.M_module_name, msg.M_func_name, len(_func.M_param), len(msg.M_func_parameter)) 121 | return nil 122 | } 123 | return i.paramUnmarshalWithTypeSlice(msg.M_func_parameter, _func.M_param) 124 | } 125 | 126 | func (i *Info) call(msg_ *YMsg.S2S_rpc_msg, val_list_ []reflect.Value) []reflect.Value { 127 | _func := i.M_rpc_func_map[msg_.M_func_name] 128 | if _func == nil { 129 | panic(string(debug.Stack())) 130 | } 131 | return _func.M_fn.Call(val_list_) 132 | } 133 | 134 | func (i *Info) DoRPCMsg(msg_ *YMsg.S2S_rpc_msg) { 135 | if msg_.M_is_back { 136 | _call_back_cmd_list := i.M_back_fun[msg_.M_uid] 137 | if _call_back_cmd_list == nil { 138 | return 139 | } 140 | _param_value := i.paramUnmarshalWithTypeSlice(msg_.M_func_parameter, _call_back_cmd_list.getCurCmd().M_back_param) 141 | _call_back_cmd_list.call(_param_value) 142 | if i.m_cb_list_cancel { 143 | i.m_cb_list_cancel = false 144 | delete(i.M_back_fun, msg_.M_uid) 145 | return 146 | } 147 | if _call_back_cmd_list.isOver() { 148 | delete(i.M_back_fun, msg_.M_uid) 149 | return 150 | } 151 | 152 | _rpc_msg := _call_back_cmd_list.popMsg() 153 | if !_rpc_msg.M_need_back { 154 | delete(i.M_back_fun, msg_.M_uid) 155 | } 156 | _rpc_msg.M_source = i.GetAgent() 157 | //_rpc_msg.M_tar = msg_.M_tar 158 | i.RPCToOther(_rpc_msg) 159 | return 160 | } 161 | 162 | _param_list := i.msgUnmarshalParamList(msg_) 163 | if _param_list == nil { 164 | return 165 | } 166 | _back_param := i.call(msg_, _param_list) 167 | if msg_.M_need_back { 168 | _back_param_inter_list := make([]interface{}, 0, len(_back_param)) 169 | for _, _it := range _back_param { 170 | _back_param_inter_list = append(_back_param_inter_list, _it.Interface()) 171 | } 172 | _rpc_cmd := NewRPCCommand(msg_.M_source, msg_.M_func_name, _back_param_inter_list...) 173 | _rpc_msg := _rpc_cmd.ToRPCMsg() 174 | _rpc_msg.M_is_back = true 175 | _rpc_msg.M_uid = msg_.M_uid 176 | i.RPCToOther(_rpc_msg) 177 | } 178 | } 179 | func (i *Info) DonNetMsg(msg_ *YMsg.C2S_net_msg) { 180 | _net_func_obj := i.M_net_func_map[msg_.M_net_msg.M_msg_name] 181 | if _net_func_obj == nil { 182 | return 183 | } 184 | 185 | _json_data := reflect.New(_net_func_obj.m_msg_data).Interface() 186 | err := jsoniter.Unmarshal(msg_.M_net_msg.M_msg_data, _json_data) 187 | if err != nil { 188 | ylog.Erro("[%v] decode err [%v]", msg_.M_net_msg.M_msg_data, err.Error()) 189 | return 190 | } 191 | 192 | _net_func_obj.M_fn.Call([]reflect.Value{ 193 | reflect.ValueOf(msg_.M_session_id), 194 | reflect.ValueOf(_json_data).Elem(), 195 | }) 196 | } 197 | 198 | func (i *Info) Loop_Msg() { 199 | for { 200 | if i.M_net_queue.Len() == 0 { 201 | break 202 | } 203 | _msg := i.M_net_queue.Pop().(*YMsg.C2S_net_msg) 204 | i.DonNetMsg(_msg) 205 | } 206 | for { 207 | if i.M_rpc_queue.Len() == 0 { 208 | break 209 | } 210 | _msg := i.M_rpc_queue.Pop().(*YMsg.S2S_rpc_msg) 211 | i.DoRPCMsg(_msg) 212 | } 213 | } 214 | 215 | func (i *Info) GetAgent() YMsg.Agent { 216 | return i.M_agent 217 | } 218 | 219 | func (i *Info) DebugString() string { 220 | return fmt.Sprintf("[%v]", i.GetAgent().DebugString()) 221 | } 222 | 223 | func (i *Info) SendNetMsgJson(session_id_ uint64, json_msg_ interface{}) { 224 | _msg := YNet.NewNetMsgPackWithJson(json_msg_) 225 | if _msg == nil { 226 | ylog.Erro("[%v:SendNetMsgJson] err [%v]", i.GetAgent().GetKeyStr(), reflect.TypeOf(json_msg_).String()) 227 | return 228 | } 229 | i.RPCCall(YMsg.ToAgent("NetModule"), "SendNetMsgJson", session_id_, _msg) 230 | } 231 | 232 | func (i *Info) RPCCall(tar_agent YMsg.Agent, func_ string, param_list_ ...interface{}) *RPCCommandList { 233 | _rpc_command_list := NewRPCCommandList() 234 | _rpc_command_list.AfterRPC(tar_agent, func_, param_list_...) 235 | _rpc_msg := _rpc_command_list.popMsg() 236 | if _rpc_msg.M_need_back { 237 | _rpc_msg.M_source = i.GetAgent() 238 | i.M_back_fun[_rpc_command_list.M_uid] = _rpc_command_list 239 | } 240 | i.RPCToOther(_rpc_msg) 241 | return _rpc_command_list 242 | } 243 | 244 | func (i *Info) RegisterModule(module_name_ string, id_ uint64) { 245 | i.RPCCall(YMsg.ToAgent("YNode"), "NewModule", module_name_, id_) 246 | } 247 | -------------------------------------------------------------------------------- /engine/YMsg/in_msg.go: -------------------------------------------------------------------------------- 1 | package YMsg 2 | 3 | import ( 4 | "fmt" 5 | "github.com/yxinyi/YCServer/engine/YNet" 6 | ) 7 | 8 | type Agent struct { 9 | M_module_name string 10 | M_module_uid uint64 11 | } 12 | 13 | type S2S_rpc_msg struct { 14 | M_uid uint64 15 | M_source Agent 16 | M_tar Agent 17 | M_marshal_type uint32 18 | M_func_name string 19 | M_func_parameter [][]byte 20 | M_need_back bool 21 | M_is_back bool 22 | } 23 | 24 | type C2S_net_msg struct { 25 | M_tar Agent 26 | M_session_id uint64 27 | M_net_msg *YNet.NetMsgPack 28 | } 29 | 30 | func (m *S2S_rpc_msg) DebugString() string { 31 | return fmt.Sprintf("Tar [%v] Func [%v]", m.M_tar.DebugString(), m.M_func_name) 32 | } 33 | 34 | type TestParam struct { 35 | M_val uint32 36 | M_val_str string 37 | M_val_int []int 38 | } 39 | 40 | func ToAgent(args ...interface{}) Agent { 41 | _agent := Agent{} 42 | if len(args) >= 1 { 43 | _agent.M_module_name = args[0].(string) 44 | } 45 | if len(args) >= 2 { 46 | _module_uid := uint64(0) 47 | switch args[1].(type) { 48 | case uint64: 49 | _module_uid = args[1].(uint64) 50 | case uint32: 51 | _module_uid = uint64(args[1].(uint32)) 52 | case int: 53 | _module_uid = uint64(args[1].(int)) 54 | } 55 | _agent.M_module_uid = _module_uid 56 | } 57 | return _agent 58 | } 59 | 60 | func (a Agent) DebugString() string { 61 | return fmt.Sprintf("Tar [%v:%v]", a.M_module_name, a.M_module_uid) 62 | } 63 | func (a Agent) GetKeyStr() string { 64 | return fmt.Sprintf("%v:%v", a.M_module_name, a.M_module_uid) 65 | } 66 | -------------------------------------------------------------------------------- /engine/YNet/Connect.go: -------------------------------------------------------------------------------- 1 | package YNet 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const ( 10 | MAX_PACKAGE_LEN uint32 = 1000 11 | ) 12 | 13 | type Connect struct { 14 | m_conn net.Conn 15 | m_session *Session 16 | m_wg sync.WaitGroup 17 | } 18 | 19 | func NewConnect() *Connect { 20 | return &Connect{} 21 | } 22 | 23 | func (c *Connect) Connect(ip_port_ string) bool { 24 | var _conn net.Conn 25 | for { 26 | _tmp_conn, _err := net.Dial("tcp4", ip_port_) 27 | if _err != nil { 28 | time.Sleep(1 * time.Second) 29 | continue 30 | } 31 | _conn = _tmp_conn 32 | break 33 | } 34 | c.m_conn = _conn 35 | c.m_conn.(*net.TCPConn).SetNoDelay(true) 36 | c.m_session = NewSession(_conn) 37 | return true 38 | } 39 | 40 | /*func (c *Connect) SendMsg(msg_id_ uint32, msg_ interface{}) { 41 | _net_msg := NewNetMsgPack() 42 | _net_msg.M_msg_id = msg_id_ 43 | json_data, err := jsoniter.Marshal(msg_) 44 | if err == nil { 45 | _net_msg.M_msg_data = json_data 46 | _net_msg.M_msg_length = uint32(len(json_data)) 47 | } 48 | c.m_session.Send(_net_msg) 49 | }*/ 50 | 51 | func (c *Connect) SendJson(msg_ interface{}) { 52 | if c.m_session == nil { 53 | return 54 | } 55 | c.m_session.SendJson(msg_) 56 | } 57 | func (c *Connect) GetSession() *Session { 58 | return c.m_session 59 | } 60 | func (c *Connect) Start() bool { 61 | c.m_session.StartLoop() 62 | return true 63 | } 64 | 65 | func (c *Connect) End() bool { 66 | c.m_session.Close() 67 | return true 68 | } 69 | -------------------------------------------------------------------------------- /engine/YNet/Listen.go: -------------------------------------------------------------------------------- 1 | package YNet 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | var g_listener net.Listener 10 | var G_close = make(chan struct{}) 11 | var g_connect_list = make(map[uint64]*Session) 12 | 13 | var G_net_msg_chan = make(chan *Message,100) 14 | 15 | 16 | func ListenTcp4(ip_port_ string) error { 17 | listen, err := net.Listen("tcp4", ip_port_) 18 | if err != nil { 19 | return errors.New("listen err [%v]" + err.Error()) 20 | } 21 | go func() { 22 | for { 23 | select { 24 | case <-G_close: 25 | return 26 | default: 27 | _conn, err := listen.Accept() 28 | if err != nil { 29 | fmt.Printf("accept err " + err.Error()) 30 | continue 31 | } 32 | _session := NewSession(_conn) 33 | g_connect_list[_session.m_uid] = _session 34 | _session.StartLoop() 35 | 36 | connMsg := NewMessage(NET_SESSION_STATE_CONNECT,_session,nil) 37 | G_net_msg_chan <- connMsg 38 | } 39 | 40 | } 41 | }() 42 | 43 | return nil 44 | } 45 | 46 | func Stop() { 47 | G_close <- struct{}{} 48 | } 49 | -------------------------------------------------------------------------------- /engine/YNet/Message.go: -------------------------------------------------------------------------------- 1 | package YNet 2 | 3 | import "sync/atomic" 4 | 5 | const ( 6 | NET_SESSION_STATE_CONNECT uint8 = iota 7 | NET_SESSION_STATE_MSG 8 | NET_SESSION_STATE_CLOSE 9 | NET_SESSION_STATE_CONNECT_OTHER_SUCCESS 10 | ) 11 | 12 | type Message struct { 13 | M_msg_type uint8 14 | M_uid uint64 15 | M_session *Session 16 | M_net_msg *NetMsgPack 17 | } 18 | 19 | var g_msg_unique_id = uint64(0) 20 | 21 | func NewMessage(type_ uint8, s_ *Session, pack_ *NetMsgPack) *Message { 22 | atomic.AddUint64(&g_msg_unique_id, 1) 23 | return &Message{ 24 | M_msg_type: type_, 25 | M_uid: g_msg_unique_id, 26 | M_session: s_, 27 | M_net_msg: pack_, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /engine/YNet/NetMsgManager.go: -------------------------------------------------------------------------------- 1 | package YNet 2 | 3 | import ( 4 | "fmt" 5 | "github.com/json-iterator/go" 6 | "reflect" 7 | "strings" 8 | ) 9 | 10 | type NetMsgHandler struct { 11 | m_msg_name string 12 | m_fn reflect.Value 13 | m_msg_data reflect.Type 14 | } 15 | 16 | var net_msg_list = make(map[string]*NetMsgHandler) 17 | 18 | func Register(fn_ interface{}) { 19 | 20 | _handler := &NetMsgHandler{ 21 | m_fn: reflect.ValueOf(fn_), 22 | } 23 | _handler.m_msg_data = reflect.TypeOf(fn_).In(1) 24 | _msg_name := _handler.m_msg_data.String() 25 | _split_idx := strings.Index(_msg_name,".") 26 | _msg_name = _msg_name[_split_idx+1:] 27 | net_msg_list[_msg_name] = _handler 28 | } 29 | 30 | func Dispatch(s_ *Session, net_msg_ *NetMsgPack) error { 31 | if net_msg_ == nil { 32 | return nil 33 | } 34 | _handler, exists := net_msg_list[net_msg_.M_msg_name] 35 | if !exists { 36 | return fmt.Errorf("[%v] miss call back ", net_msg_.M_msg_name) 37 | } 38 | fmt.Printf("[Dispatch] [%v] \n",net_msg_.M_msg_name) 39 | //可以传入不同的解析类型,进行解析 40 | _json_data := reflect.New(_handler.m_msg_data).Interface() 41 | err := jsoniter.Unmarshal(net_msg_.M_msg_data, _json_data) 42 | if err != nil { 43 | return fmt.Errorf("[%v] decode err [%v]", net_msg_.M_msg_data, err.Error()) 44 | } 45 | 46 | _handler.m_fn.Call([]reflect.Value{ 47 | reflect.ValueOf(s_).Elem(), 48 | reflect.ValueOf(_json_data).Elem(), 49 | }) 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /engine/YNet/NetMsgPack.go: -------------------------------------------------------------------------------- 1 | package YNet 2 | 3 | import ( 4 | "encoding/binary" 5 | "github.com/json-iterator/go" 6 | "io" 7 | "reflect" 8 | "strings" 9 | "unsafe" 10 | ) 11 | 12 | const ( 13 | MESSAGE_TYPE_JSON = 1 14 | MESSAGE_TYPE_PROTO = 2 15 | ) 16 | 17 | type NetMsgPack struct { 18 | M_msg_name string 19 | M_msg_data []byte 20 | } 21 | 22 | const ( 23 | const_type_size = int(unsafe.Sizeof(uint32(0))) 24 | const_length_size = int(unsafe.Sizeof(uint32(0))) 25 | ) 26 | 27 | func NewNetMsgPack() *NetMsgPack { 28 | return &NetMsgPack{ 29 | } 30 | } 31 | func NewNetMsgPackWithJson(json_ interface{}) *NetMsgPack { 32 | _msg := NewNetMsgPack() 33 | 34 | _msg_name := reflect.TypeOf(json_).String() 35 | _split_idx := strings.Index(_msg_name,".") 36 | _msg_name = _msg_name[_split_idx+1:] 37 | _msg.M_msg_name = _msg_name 38 | _byte, _err := jsoniter.Marshal(json_) 39 | if _err != nil { 40 | return nil 41 | } 42 | _msg.M_msg_data = _byte 43 | return _msg 44 | } 45 | 46 | func (pack *NetMsgPack) ToByteStream() []byte { 47 | _name_length := len(pack.M_msg_name) 48 | _data_length := len(pack.M_msg_data) 49 | 50 | _total_length := const_type_size + const_length_size + _name_length + _data_length 51 | _stream_byte := make([]byte, _total_length) 52 | 53 | binary.LittleEndian.PutUint32(_stream_byte, uint32(_name_length)) 54 | binary.LittleEndian.PutUint32(_stream_byte[const_type_size:], uint32(_data_length)) 55 | copy(_stream_byte[uint32(const_type_size+const_length_size):], pack.M_msg_name[:]) 56 | copy(_stream_byte[uint32(const_type_size+const_length_size+_name_length):], pack.M_msg_data[:]) 57 | return _stream_byte 58 | } 59 | 60 | func (pack *NetMsgPack) InitFromIO(io_ io.Reader) bool { 61 | _type_byte := make([]byte, const_type_size+const_length_size) 62 | 63 | _len, err := io.ReadFull(io_, _type_byte) 64 | if _len == 0 { 65 | return false 66 | } 67 | if err != nil { 68 | return false 69 | } 70 | 71 | _name_length := binary.LittleEndian.Uint32(_type_byte[0:const_type_size]) 72 | _data_length := binary.LittleEndian.Uint32(_type_byte[const_type_size : const_type_size+const_type_size]) 73 | 74 | _msg_name_byte := make([]byte, _name_length) 75 | _len, err = io.ReadFull(io_, _msg_name_byte) 76 | if err != nil { 77 | return false 78 | } 79 | pack.M_msg_name = string(_msg_name_byte) 80 | 81 | pack.M_msg_data = make([]byte, _data_length) 82 | _len, err = io.ReadFull(io_, pack.M_msg_data) 83 | if err != nil { 84 | return false 85 | } 86 | 87 | return true 88 | } 89 | -------------------------------------------------------------------------------- /engine/YNet/Session.go: -------------------------------------------------------------------------------- 1 | package YNet 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync/atomic" 7 | ) 8 | 9 | type Session struct { 10 | m_uid uint64 11 | m_conn net.Conn 12 | m_stop chan struct{} 13 | m_send_msg_chan chan *NetMsgPack 14 | M_is_rotbot bool 15 | } 16 | 17 | var g_uni_id = uint64(0) 18 | 19 | func NewSession(conn_ net.Conn) *Session { 20 | atomic.AddUint64(&g_uni_id, 1) 21 | return &Session{ 22 | m_uid: g_uni_id, 23 | m_conn: conn_, 24 | m_send_msg_chan: make(chan *NetMsgPack, MAX_PACKAGE_LEN), 25 | m_stop: make(chan struct{}), 26 | } 27 | } 28 | 29 | func (s *Session) GetUID() uint64 { 30 | return s.m_uid 31 | } 32 | 33 | func (s *Session) Close() { 34 | close(s.m_send_msg_chan) 35 | close(s.m_stop) 36 | } 37 | 38 | func (s *Session) SendJson(json_ interface{}) error { 39 | if s.M_is_rotbot { 40 | return nil 41 | } 42 | _msg := NewNetMsgPackWithJson(json_) 43 | if _msg == nil { 44 | return fmt.Errorf("[Session:SendJson] pack error") 45 | } 46 | s.Send(_msg) 47 | return nil 48 | } 49 | 50 | func (s *Session) Send(msg_ *NetMsgPack) bool { 51 | if s.M_is_rotbot { 52 | return true 53 | } 54 | if s.m_send_msg_chan == nil { 55 | return false 56 | } 57 | s.m_send_msg_chan <- msg_ 58 | return true 59 | } 60 | 61 | func (s *Session) StartLoop() { 62 | go func() { 63 | for { 64 | select { 65 | case <-s.m_stop: 66 | return 67 | case pack, ok := <-s.m_send_msg_chan: 68 | if !ok { 69 | s.m_conn.Close() 70 | return 71 | } 72 | _msg_byte := pack.ToByteStream() 73 | _, err := s.m_conn.Write(_msg_byte) 74 | if err != nil { 75 | break 76 | } 77 | } 78 | } 79 | }() 80 | go func() { 81 | for { 82 | select { 83 | case <-s.m_stop: 84 | return 85 | default: 86 | _msg_pack := NewNetMsgPack() 87 | if !_msg_pack.InitFromIO(s.m_conn) { 88 | connMsg := NewMessage(NET_SESSION_STATE_CLOSE, s, nil) 89 | G_net_msg_chan <- connMsg 90 | return 91 | } 92 | 93 | connMsg := NewMessage(NET_SESSION_STATE_MSG, s, _msg_pack) 94 | G_net_msg_chan <- connMsg 95 | } 96 | } 97 | }() 98 | } 99 | -------------------------------------------------------------------------------- /engine/YNode/API.go: -------------------------------------------------------------------------------- 1 | package YNode 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YJson" 5 | ylog "github.com/yxinyi/YCServer/engine/YLog" 6 | "github.com/yxinyi/YCServer/engine/YMsg" 7 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 8 | ) 9 | 10 | func (n *Info) RPC_NewModule(create_func_name string, uid_ uint64) { 11 | _new_module := NewModuleInfo(create_func_name, uid_) 12 | G_node_obj.register(_new_module) 13 | //n.SyncModulesKey() 14 | go G_node_obj.startModule(_new_module) 15 | } 16 | 17 | func (n *Info) SyncModulesKey(session_ uint64) { 18 | _keys := make([]string, 0) 19 | for _, _mo_it := range n.M_module_pool { 20 | _keys = append(_keys, _mo_it.GetInfo().GetAgent().GetKeyStr()) 21 | } 22 | 23 | n.SendNetMsgJson(session_, Msg.S2S_SyncOtherNodeRPC{ 24 | _keys, 25 | n.M_node_id, 26 | }) 27 | } 28 | 29 | func (n *Info) RPC_RegisterOtherNode(ip_port_ string, session_ uint64) { 30 | _node_id, exists := n.M_node_ip_to_id[ip_port_] 31 | if !exists { 32 | n.RPCCall(YMsg.ToAgent("NetModule", uint64(n.M_node_id)), "Close", session_) 33 | return 34 | } 35 | n.M_node_id_to_session[_node_id] = session_ 36 | n.SyncModulesKey(session_) 37 | } 38 | 39 | func (n *Info) MSG_S2S_SyncOtherNodeRPC(s_ uint64, msg_ Msg.S2S_SyncOtherNodeRPC) { 40 | ylog.Info("[%v]", YJson.GetPrintStr(msg_)) 41 | for _, _msg_it := range msg_.M_net_msg { 42 | n.M_other_node_module_key_str_to_node_id[_msg_it] = msg_.M_node_id 43 | } 44 | } 45 | 46 | func (n *Info) MSG_C2S_net_msg(s_ uint64, msg_ YMsg.C2S_net_msg) { 47 | n.PushNetMsg(&msg_) 48 | } 49 | -------------------------------------------------------------------------------- /engine/YNode/Info.go: -------------------------------------------------------------------------------- 1 | package YNode 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YModule" 5 | ) 6 | 7 | type Info struct { 8 | M_node_id uint32 9 | M_module_pool map[string]YModule.Inter 10 | 11 | M_node_ip_to_id map[string]uint32 12 | M_node_id_to_session map[uint32]uint64 13 | 14 | M_other_node_module_key_str_to_node_id map[string]uint32 15 | 16 | YModule.BaseInter 17 | 18 | m_moduele_factory map[string]func(node_ *Info, uid uint64) YModule.Inter 19 | } 20 | 21 | func newInfo() *Info { 22 | info := &Info{ 23 | M_module_pool: make(map[string]YModule.Inter), 24 | m_moduele_factory: make(map[string]func(node_ *Info, uid uint64) YModule.Inter), 25 | 26 | M_node_ip_to_id: make(map[string]uint32), 27 | M_node_id_to_session: make(map[uint32]uint64), 28 | 29 | M_other_node_module_key_str_to_node_id: make(map[string]uint32), 30 | } 31 | return info 32 | } 33 | -------------------------------------------------------------------------------- /engine/YNode/Node.go: -------------------------------------------------------------------------------- 1 | package YNode 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YMsg" 7 | "reflect" 8 | "runtime/debug" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | var G_node_obj = newInfo() 14 | var g_stop = make(chan struct{}) 15 | 16 | func init() { 17 | G_node_obj.Info = YModule.NewInfo(G_node_obj) 18 | } 19 | 20 | func (n *Info) findNode(key_str_ string) uint64 { 21 | _node_id, _exists := n.M_other_node_module_key_str_to_node_id[key_str_] 22 | if !_exists { 23 | return 0 24 | } 25 | 26 | return n.M_node_id_to_session[_node_id] 27 | } 28 | 29 | /*func (n *Info) GetModuleName(module_name_ string, module_uid_ uint64) string { 30 | return fmt.Sprintf("%s:%d", module_name_, module_uid_) 31 | }*/ 32 | 33 | func (n *Info) register(info YModule.Inter) { 34 | G_node_obj.M_module_pool[info.GetInfo().GetAgent().GetKeyStr()] = info 35 | info.Init() 36 | } 37 | 38 | func (n *Info) RPCToOther(msg *YMsg.S2S_rpc_msg) { 39 | G_node_obj.PushRpcMsg(msg) 40 | } 41 | func (n *Info) NetToOther(msg *YMsg.C2S_net_msg) { 42 | G_node_obj.PushNetMsg(msg) 43 | } 44 | 45 | func (n *Info) dispatchNet(msg_ *YMsg.C2S_net_msg) bool { 46 | _tar_key_st := msg_.M_tar.GetKeyStr() 47 | _, exists := G_node_obj.M_module_pool[_tar_key_st] 48 | if !exists { 49 | ylog.Erro("[YNode:dispatchNet] miss module uid [%v]", _tar_key_st) 50 | return false 51 | } 52 | G_node_obj.M_module_pool[_tar_key_st].GetInfo().PushNetMsg(msg_) 53 | return true 54 | } 55 | 56 | func (n *Info) dispatchRpc(msg_ *YMsg.S2S_rpc_msg) bool { 57 | _module_name_uid_str := msg_.M_tar.GetKeyStr() 58 | _, exists := G_node_obj.M_module_pool[_module_name_uid_str] 59 | if !exists { 60 | ylog.Erro("[YNode:dispatchRpc] miss module uid [%v][%v]", G_node_obj.M_module_pool,_module_name_uid_str) 61 | return false 62 | } 63 | 64 | //ylog.Info("[Node:%v] dispatch RPC [%v]", G_node_obj.M_module_pool[msg_.M_tar.M_entity_name][msg_.M_tar.M_node_id].GetInfo().M_entity_name, msg_.M_func_name) 65 | G_node_obj.M_module_pool[_module_name_uid_str].GetInfo().PushRpcMsg(msg_) 66 | return true 67 | } 68 | 69 | func (n *Info) close() { 70 | for _, _module_it := range G_node_obj.M_module_pool { 71 | _module_it.Close() 72 | } 73 | } 74 | func (n *Info) startModule(module_ YModule.Inter) { 75 | defer func() { 76 | if err := recover(); err != nil { 77 | ylog.Erro("%v", err) 78 | ylog.Erro("stack:%s", debug.Stack()) 79 | } 80 | }() 81 | 82 | _100_last_print_time := time.Now().Unix() 83 | _10_last_print_time := time.Now().Unix() 84 | _1_last_print_time := time.Now().Unix() 85 | _100_fps_count := 0 86 | _10_fps_count := 0 87 | _1_fps_count := 0 88 | 89 | /////////// 90 | 91 | _100_fps_timer := time.NewTicker(time.Millisecond * 10) 92 | defer _100_fps_timer.Stop() 93 | _10_fps_timer := time.NewTicker(time.Millisecond * 100) 94 | defer _10_fps_timer.Stop() 95 | _1_fps_timer := time.NewTicker(time.Millisecond * 1000) 96 | defer _10_fps_timer.Stop() 97 | for { 98 | select { 99 | case _time := <-_100_fps_timer.C: 100 | _100_fps_count++ 101 | module_.Loop_100(_time) 102 | module_.GetInfo().Loop_Msg() 103 | if (_time.Unix() - _100_last_print_time) >= 60 { 104 | _second_fps := _100_fps_count / int(_time.Unix()-_100_last_print_time) 105 | if _second_fps < 80 { 106 | ylog.Erro("[Module:%v] 100 fps [%v]", module_.GetInfo().GetAgent().DebugString(), _100_fps_count/int(_time.Unix()-_100_last_print_time)) 107 | } 108 | _100_last_print_time = _time.Unix() 109 | _100_fps_count = 0 110 | } 111 | case _time := <-_10_fps_timer.C: 112 | _10_fps_count++ 113 | module_.Loop_10(_time) 114 | if (_time.Unix() - _10_last_print_time) >= 60 { 115 | _second_fps := _10_fps_count / int(_time.Unix()-_10_last_print_time) 116 | if _second_fps < 8 { 117 | ylog.Info("[Module:%v] 10 fps [%v]", module_.GetInfo().GetAgent().DebugString(), _10_fps_count/int(_time.Unix()-_10_last_print_time)) 118 | } 119 | 120 | _10_last_print_time = _time.Unix() 121 | _10_fps_count = 0 122 | } 123 | case _time := <-_1_fps_timer.C: 124 | _1_fps_count++ 125 | module_.Loop_1(_time) 126 | if (_time.Unix() - _1_last_print_time) >= 60 { 127 | _second_fps := _1_fps_count / int(_time.Unix()-_1_last_print_time) 128 | if _second_fps < 1 { 129 | ylog.Info("[Module:%v] 10 fps [%v]", module_.GetInfo().GetAgent().DebugString(), _1_fps_count/int(_time.Unix()-_1_last_print_time)) 130 | } 131 | 132 | _1_last_print_time = _time.Unix() 133 | _1_fps_count = 0 134 | } 135 | } 136 | 137 | } 138 | } 139 | func (n *Info) start() { 140 | for _, _module_it := range G_node_obj.M_module_pool { 141 | go n.startModule(_module_it) 142 | } 143 | //主逻辑 144 | G_node_obj.register(G_node_obj) 145 | G_node_obj.GetInfo().Init(G_node_obj) 146 | n.loop() 147 | } 148 | 149 | func (n *Info) loop() { 150 | for { 151 | select { 152 | case <-g_stop: 153 | return 154 | default: 155 | 156 | if G_node_obj.M_net_queue.Len() > 0 { 157 | for { 158 | if G_node_obj.M_net_queue.Len() == 0 { 159 | break 160 | } 161 | _msg := G_node_obj.M_net_queue.Pop().(*YMsg.C2S_net_msg) 162 | if _msg.M_tar.M_module_name == "YNode" { 163 | n.DonNetMsg(_msg) 164 | continue 165 | } 166 | if n.dispatchNet(_msg) { 167 | continue 168 | } 169 | } 170 | } 171 | 172 | if G_node_obj.M_rpc_queue.Len() > 0 { 173 | for { 174 | if G_node_obj.M_rpc_queue.Len() == 0 { 175 | break 176 | } 177 | //ylog.Info("[Node:RPC_QUEUE] [%v]", G_node_obj.M_rpc_queue.Len()) 178 | _msg := G_node_obj.M_rpc_queue.Pop().(*YMsg.S2S_rpc_msg) 179 | if _msg.M_tar.M_module_name == "YNode" { 180 | n.DoRPCMsg(_msg) 181 | continue 182 | } 183 | if n.dispatchRpc(_msg) { 184 | continue 185 | } 186 | G_node_obj.Info.SendNetMsgJson(n.findNode(_msg.M_tar.GetKeyStr()), *_msg) 187 | } 188 | } 189 | } 190 | } 191 | } 192 | 193 | func Register(info_list_ ...YModule.Inter) { 194 | for _, _it := range info_list_ { 195 | G_node_obj.register(_it) 196 | } 197 | } 198 | 199 | func RPCCall(msg_ *YMsg.S2S_rpc_msg) { 200 | 201 | G_node_obj.RPCToOther(msg_) 202 | } 203 | 204 | func Obj() *Info { 205 | return G_node_obj 206 | } 207 | 208 | func Close() { 209 | G_node_obj.close() 210 | } 211 | 212 | func Start() { 213 | G_node_obj.start() 214 | } 215 | 216 | func ModuleCreateFuncRegister(name_ string, func_ func(node_ *Info, uid uint64) YModule.Inter) { 217 | G_node_obj.registerToFactory(name_, func_) 218 | } 219 | 220 | func (n *Info) registerToFactory(name_ string, func_ func(node_ *Info, uid uint64) YModule.Inter) { 221 | n.m_moduele_factory[name_] = func_ 222 | } 223 | 224 | func RegisterNodeIpStr2NodeId(ip_port_ string, node_id_ uint32) { 225 | G_node_obj.M_node_ip_to_id[ip_port_] = node_id_ 226 | } 227 | 228 | func SetNodeID(node_id_ uint32) { 229 | G_node_obj.M_node_id = node_id_ 230 | G_node_obj.GetInfo().M_agent = YMsg.ToAgent("YNode", uint64(0)) 231 | } 232 | 233 | func NewModuleInfo(module_str_ string, module_uid_ uint64) YModule.Inter { 234 | _new_module := G_node_obj.m_moduele_factory[module_str_](G_node_obj, module_uid_) 235 | _new_module.GetInfo().M_agent = YMsg.ToAgent(strings.Split(reflect.TypeOf(_new_module).Elem().String(), ".")[0], module_uid_) 236 | return _new_module 237 | } 238 | -------------------------------------------------------------------------------- /engine/YPathFinding/GoAStarManager.go: -------------------------------------------------------------------------------- 1 | package YPathFinding 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YTool" 6 | ) 7 | 8 | type aStarCallbackMsg struct { 9 | m_uid uint32 10 | m_st int 11 | m_ed int 12 | m_search_path []int 13 | } 14 | 15 | func NewAStarCallbackMsg() *aStarCallbackMsg { 16 | return &aStarCallbackMsg{} 17 | } 18 | 19 | func (a *aStarCallbackMsg) Init(uid_ uint32, st_, ed_ int, search_path_ []int) { 20 | a.m_uid = uid_ 21 | a.m_st = st_ 22 | a.m_ed = ed_ 23 | a.m_search_path = search_path_ 24 | } 25 | 26 | type aStarCallback func([]int) 27 | 28 | type AStarManager struct { 29 | M_map_uid uint64 30 | m_maze [][]float64 31 | m_queue *YTool.SyncQueue 32 | 33 | m_cache_path map[uint64][]int 34 | 35 | m_call_back_idx uint32 36 | m_call_back map[uint32]aStarCallback 37 | } 38 | 39 | func NewAStarManager() *AStarManager { 40 | return &AStarManager{ 41 | m_queue: YTool.NewSyncQueue(), 42 | m_call_back: make(map[uint32]aStarCallback), 43 | m_cache_path: make(map[uint64][]int), 44 | } 45 | } 46 | 47 | func (mgr *AStarManager) GetMaze() [][]float64 { 48 | return mgr.m_maze 49 | } 50 | 51 | func (mgr *AStarManager) Init(maze_ [][]float64) { 52 | mgr.m_maze = maze_ 53 | } 54 | 55 | func (mgr *AStarManager) IsBlock(index_ int) bool { 56 | _col := index_ / len(mgr.m_maze[0]) 57 | _row := index_ % len(mgr.m_maze[0]) 58 | if _col < 0 || _col > len(mgr.m_maze)-1 { 59 | return false 60 | } 61 | if _row < 0 || _row > len(mgr.m_maze[0])-1 { 62 | return false 63 | } 64 | return mgr.m_maze[_col][_row] != 0 65 | } 66 | 67 | func (mgr *AStarManager) Search(st_, ed_ int, cb_ aStarCallback) { 68 | ylog.Info("[%v:%v]", st_, ed_) 69 | /* _cache_path, exists := mgr.m_cache_path[uint64(st_)<<32|uint64(ed_)] 70 | if exists { 71 | cb_(_cache_path) 72 | return append(_final_path, before_path_[_loop_idx]) 73 | }*/ 74 | 75 | _tmp_idx := mgr.m_call_back_idx 76 | mgr.m_call_back_idx++ 77 | mgr.m_call_back[_tmp_idx] = cb_ 78 | go func() { 79 | _a := NewAStar() 80 | _a.Init(mgr.m_maze) 81 | _ret := _a.SearchBetterWithIndex(st_, ed_) 82 | _msg := NewAStarCallbackMsg() 83 | _msg.Init(_tmp_idx, st_, ed_, _ret) 84 | mgr.m_queue.Add(_msg) 85 | }() 86 | } 87 | 88 | func (mgr *AStarManager) Update() { 89 | for { 90 | if mgr.m_queue.Len() == 0 { 91 | break 92 | } 93 | _msg := mgr.m_queue.Pop().(*aStarCallbackMsg) 94 | mgr.m_call_back[_msg.m_uid](_msg.m_search_path) 95 | /* ylog.Info("###### [%v:%v]", _msg.m_st, _msg.m_ed) 96 | mgr.m_cache_path[uint64(_msg.m_st)<<32|uint64(_msg.m_ed)] = _msg.m_search_path 97 | for _idx, _path_node_it := range _msg.m_search_path { 98 | mgr.m_cache_path[uint64(_path_node_it)<<32|uint64(_msg.m_ed)] = _msg.m_search_path[_idx:] 99 | } 100 | */ 101 | delete(mgr.m_call_back, _msg.m_uid) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /engine/YTimer/Timer.go: -------------------------------------------------------------------------------- 1 | package YTimer 2 | 3 | var time_uid_count uint32 = 0 4 | 5 | type Timer struct { 6 | m_perv *Timer 7 | m_next *Timer 8 | m_uid uint32 9 | M_interval int64 10 | M_call_time int64 11 | M_times int 12 | M_slot uint32 13 | M_callback TimerCallBack 14 | } 15 | 16 | func newTimer() *Timer { 17 | time_uid_count++ 18 | return &Timer{ 19 | m_uid: time_uid_count, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /engine/YTimer/TimerAPI.go: -------------------------------------------------------------------------------- 1 | package YTimer 2 | 3 | import "time" 4 | 5 | func TimerCall(t_ *Timer) uint32 { 6 | g_add_timer_channel <- t_ 7 | return t_.m_uid 8 | } 9 | 10 | func Close() { 11 | g_close <- struct{}{} 12 | } 13 | func CancelTimer(uid_ uint32) { 14 | g_timer_manager.cancelTimer(uid_) 15 | } 16 | 17 | func convertToTickUnit(t_ YSecond) int64 { 18 | return int64((float64(t_)) * float64(int64(time.Second.Nanoseconds())/int64(TickerTime))) 19 | } 20 | 21 | // *秒后执行 22 | func AfterSecondsCall(after_time_ YSecond, cb_ TimerCallBack) uint32 { 23 | _t := newTimer() 24 | _t.M_callback = cb_ 25 | _t.M_call_time = convertToTickUnit(after_time_ + YSecond(time.Now().Unix())) 26 | g_add_timer_channel <- _t 27 | return _t.m_uid 28 | } 29 | 30 | // *秒后执行,每隔*秒执行*次 31 | func AfterSecondsWithIntervalAndLoopTimesCall(after_time_ YSecond, inter_val_ YSecond, loop_times int, cb_ TimerCallBack) uint32 { 32 | _t := newTimer() 33 | _t.M_callback = cb_ 34 | _t.M_times = loop_times 35 | _t.M_interval = convertToTickUnit(inter_val_) 36 | _t.M_call_time = convertToTickUnit(after_time_ + YSecond(time.Now().Unix())) 37 | g_add_timer_channel <- _t 38 | return _t.m_uid 39 | } 40 | 41 | // *秒后执行,每隔*秒执行 42 | func AfterSecondsWithIntervalCall(after_time_ YSecond, inter_val_ YSecond, cb_ TimerCallBack) uint32 { 43 | _t := newTimer() 44 | _t.M_callback = cb_ 45 | _t.M_times = -1 46 | _t.M_interval = convertToTickUnit(inter_val_) 47 | _t.M_call_time = convertToTickUnit(after_time_ + YSecond(time.Now().Unix())) 48 | g_add_timer_channel <- _t 49 | return _t.m_uid 50 | } 51 | 52 | // 在 * 时执行 53 | func WhenTimeCall(timestamp_ YSecond, cb_ TimerCallBack) uint32 { 54 | _t := newTimer() 55 | _t.M_callback = cb_ 56 | _t.M_times = -1 57 | _t.M_call_time = convertToTickUnit(timestamp_) 58 | g_add_timer_channel <- _t 59 | return _t.m_uid 60 | } 61 | 62 | // 在 * 时执行,每隔*秒执行 63 | func WhenTimeWithIntervalCall(timestamp_ YSecond, inter_val_ YSecond, cb_ TimerCallBack) uint32 { 64 | _t := newTimer() 65 | _t.M_callback = cb_ 66 | _t.M_times = -1 67 | _t.M_interval = convertToTickUnit(inter_val_) 68 | _t.M_call_time = convertToTickUnit(timestamp_) 69 | g_add_timer_channel <- _t 70 | return _t.m_uid 71 | } 72 | 73 | // 在 * 时执行,每隔*秒执行 *次 74 | func WhenTimeWithIntervalAndLoopTimesCall(timestamp_ YSecond, inter_val_ YSecond, loop_times int, cb_ TimerCallBack) uint32 { 75 | _t := newTimer() 76 | _t.M_callback = cb_ 77 | _t.M_times = loop_times 78 | _t.M_interval = convertToTickUnit(inter_val_) 79 | _t.M_call_time = convertToTickUnit(timestamp_) 80 | g_add_timer_channel <- _t 81 | return _t.m_uid 82 | } 83 | 84 | // 每天 * 点执行 85 | func EveryDayCall(timestamp_ YSecond, cb_ TimerCallBack) uint32 { 86 | _t := newTimer() 87 | _t.M_callback = cb_ 88 | _t.M_call_time = convertToTickUnit(timestamp_) 89 | g_add_timer_channel <- _t 90 | return _t.m_uid 91 | } 92 | 93 | // 每天 * 点到 *点 间隔*秒执行 94 | func TimeToTimeWithIntervalCall(start_timestamp_ YSecond, end_timestamp_ YSecond, inter_val_ YSecond, cb_ TimerCallBack) uint32 { 95 | _t := newTimer() 96 | _t.M_callback = cb_ 97 | _t.M_call_time = convertToTickUnit(start_timestamp_) 98 | _t.M_times = int((end_timestamp_-start_timestamp_) / inter_val_) 99 | _t.M_interval = convertToTickUnit(inter_val_) 100 | g_add_timer_channel <- _t 101 | return _t.m_uid 102 | } 103 | func Loop(_timer_list *ChanTimer) { 104 | g_timer_manager.loop(_timer_list) 105 | } 106 | 107 | func GetTimerSize() uint32 { 108 | return g_timer_manager.getTimerSize() 109 | } 110 | -------------------------------------------------------------------------------- /engine/YTimer/WheelTimer.go: -------------------------------------------------------------------------------- 1 | package YTimer 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type TimerCallBack func() 10 | 11 | var g_timer_manager timerManagerInter 12 | var g_add_timer_channel = make(chan *Timer, 100) 13 | var g_cancel_timer = make(chan uint32, 100) 14 | 15 | var G_call = make(chan *ChanTimer, 100) 16 | 17 | var g_close = make(chan struct{}) 18 | 19 | const ( 20 | WheelSlotCount = 3 21 | 22 | TickerTime time.Duration = time.Millisecond*100 23 | ) 24 | type YSecond = float64 25 | 26 | type wheelTimer struct { 27 | m_slots []*Timer 28 | m_map map[uint32]*Timer 29 | m_cursor uint32 30 | m_cur_time time.Time 31 | } 32 | type ChanTimer struct { 33 | M_timer_list []*Timer 34 | M_tick_time time.Time 35 | } 36 | 37 | type timerManagerInter interface { 38 | timeCall(t_ *Timer) 39 | getAllCall() *ChanTimer 40 | setTime(t_ time.Time) 41 | getCurTime() *time.Time 42 | cancelTimer(uint32) 43 | close() 44 | getNextCursor() uint32 45 | getSlotSize() uint32 46 | getTimerSize() uint32 47 | loop(*ChanTimer) 48 | } 49 | 50 | func NewWheelTimer(slot_count_ int) { 51 | _wheel_timer := &wheelTimer{ 52 | m_slots: make([]*Timer, slot_count_), 53 | m_map: make(map[uint32]*Timer), 54 | } 55 | for _idx := range _wheel_timer.m_slots { 56 | _wheel_timer.m_slots[_idx] = newTimer() 57 | } 58 | g_timer_manager = _wheel_timer 59 | _wg := &sync.WaitGroup{} 60 | _wg.Add(1) 61 | go func() { 62 | g_timer_manager.setTime(time.Now()) 63 | ticker := time.Tick(TickerTime) 64 | _wg.Done() 65 | for { 66 | select { 67 | case _time := <-ticker: 68 | g_timer_manager.setTime(_time) 69 | 70 | G_call <- g_timer_manager.getAllCall() 71 | case _timer := <-g_add_timer_channel: 72 | g_timer_manager.timeCall(_timer) 73 | case _cancel_uid := <-g_cancel_timer: 74 | g_timer_manager.cancelTimer(_cancel_uid) 75 | case <-g_close: 76 | return 77 | default: 78 | } 79 | } 80 | }() 81 | _wg.Wait() 82 | } 83 | 84 | func (t *wheelTimer) getSlot(timestamp int64) uint32 { 85 | _diff_tm := timestamp - convertToTickUnit(float64(t.m_cur_time.Unix())) 86 | _future_slot := (int64(_diff_tm) + int64(t.m_cursor)) % int64(t.getSlotSize()) 87 | //fmt.Printf("timestamp[%v] t.m_cur_time.Unix() [%v] diff [%v] slot [%v] \n",timestamp,t.m_cur_time.Unix(),_diff_tm,_future_slot) 88 | fmt.Printf("_future_slot [%d] _diff_tm[%v]\n", _future_slot,_diff_tm) 89 | return uint32(_future_slot) 90 | } 91 | func (t *wheelTimer) cancelTimer(t_ uint32) { 92 | _timer, exists := t.m_map[t_] 93 | if !exists { 94 | return 95 | } 96 | if _timer.m_perv != nil { 97 | _timer.m_perv.m_next = _timer.m_next 98 | } 99 | if _timer.m_next != nil { 100 | _timer.m_next.m_perv = _timer.m_perv 101 | } 102 | _timer.m_perv = nil 103 | _timer.m_next = nil 104 | } 105 | 106 | func (t *wheelTimer) setTime(t_ time.Time) { 107 | t.m_cur_time = t_ 108 | //fmt.Printf("setTime [%v]\n", t.m_cur_time.Unix()) 109 | 110 | } 111 | 112 | func (t *wheelTimer) insertSlot(slot_ uint32, t_ *Timer) { 113 | t.m_map[t_.m_uid] = t_ 114 | //fmt.Printf("cur [%v] insertSlot [%v]",t.m_cursor,slot_) 115 | _root := t.m_slots[slot_] 116 | if _root.m_next != nil { 117 | _root.m_next.m_perv = t_ 118 | } 119 | t_.m_perv = _root 120 | t_.m_next = _root.m_next 121 | _root.m_next = t_ 122 | t_.M_slot = slot_ 123 | } 124 | 125 | func (t *wheelTimer) timeCall(t_ *Timer) { 126 | if t_.M_call_time <= convertToTickUnit(float64(t.m_cur_time.Unix())) { 127 | t.insertSlot(t.m_cursor+1, t_) 128 | return 129 | } 130 | t.insertSlot(t.getSlot(t_.M_call_time), t_) 131 | } 132 | func (t *wheelTimer) getSlotSize() uint32 { 133 | return uint32(len(t.m_slots)) 134 | } 135 | func (t *wheelTimer) getNextCursor() uint32 { 136 | t.m_cursor++ 137 | t.m_cursor %= t.getSlotSize() 138 | //fmt.Printf("t.m_cursor [%v] \n", t.m_cursor) 139 | return t.m_cursor 140 | } 141 | 142 | func (t *wheelTimer) getTimerSize() uint32 { 143 | return uint32(len(t.m_map)) 144 | } 145 | 146 | func (t *wheelTimer) close() { 147 | g_close <- struct{}{} 148 | } 149 | 150 | func (t *wheelTimer) getCurTime() *time.Time { 151 | return &t.m_cur_time 152 | } 153 | func (t *wheelTimer) getAllCall() *ChanTimer { 154 | _timer_list := make([]*Timer, 0) 155 | _next_cursor := t.getNextCursor() 156 | _root := t.m_slots[_next_cursor].m_next 157 | _now_time := convertToTickUnit(float64(t.m_cur_time.Unix())) 158 | for _root != nil { 159 | if _root.M_call_time > _now_time { 160 | _root = _root.m_next 161 | continue 162 | } 163 | _append_timer := _root 164 | 165 | _timer_list = append(_timer_list, _append_timer) 166 | delete(t.m_map, _append_timer.m_uid) 167 | if _root.m_perv != nil { 168 | _root.m_perv.m_next = _root.m_next 169 | } 170 | if _root.m_next != nil { 171 | _root.m_next.m_perv = _root.m_perv 172 | } 173 | _append_timer.m_next = nil 174 | _append_timer.m_perv = nil 175 | _root = _root.m_next 176 | } 177 | //t.m_slots[_next_cursor].m_next = _root 178 | return &ChanTimer{ 179 | M_timer_list: _timer_list, 180 | M_tick_time: t.m_cur_time, 181 | } 182 | } 183 | func (t *wheelTimer) loop(_timer_list *ChanTimer) { 184 | for _, _it := range _timer_list.M_timer_list { 185 | _it.M_callback() 186 | if _it.M_times == -1 { 187 | _it.M_call_time += _it.M_interval 188 | TimerCall(_it) 189 | continue 190 | } else if _it.M_times > 0 { 191 | _it.M_times-- 192 | _it.M_call_time += _it.M_interval 193 | TimerCall(_it) 194 | continue 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /engine/YTimer/timer_test.go: -------------------------------------------------------------------------------- 1 | package YTimer 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func init() { 10 | NewWheelTimer(WheelSlotCount) 11 | } 12 | 13 | func TestTimerIndexForeach(t_ *testing.T) { 14 | for _idx := 0; _idx < 20; _idx++ { 15 | if g_timer_manager.getNextCursor() != (uint32(_idx+1) % g_timer_manager.getSlotSize()) { 16 | t_.Errorf("[%v]", g_timer_manager.getNextCursor()) 17 | } 18 | 19 | } 20 | } 21 | func TimerApiHelp(t_ *testing.T, second_ float64) { 22 | _before_time := time.Now() 23 | _close := make(chan struct{}) 24 | AfterSecondsCall(second_, func() { 25 | _diff_time := time.Now().Sub(_before_time) 26 | if math.Abs(_diff_time.Seconds()-second_) > 0.01 { 27 | t_.Errorf("err diff [%v] right diff [%v] _before_time[%v] after_time [%v]", int(_diff_time.Seconds()), second_, _before_time.Unix(), _diff_time.Seconds()) 28 | } 29 | t_.Logf("TimerSize [%v]", GetTimerSize()) 30 | if GetTimerSize() != 0 { 31 | t_.Errorf("err size [%v]", GetTimerSize()) 32 | } 33 | 34 | t_.Logf("diff [%v] right diff [%v] _before_time[%v] after_time [%v]", int(_diff_time.Seconds()), second_, _before_time.Unix(), _diff_time.Seconds()) 35 | close(_close) 36 | }) 37 | for { 38 | select { 39 | case _t := <-G_call: 40 | Loop(_t) 41 | case <-_close: 42 | return 43 | } 44 | } 45 | } 46 | func TestTimerApi(t_ *testing.T) { 47 | TimerApiHelp(t_, 1) 48 | TimerApiHelp(t_, 1) 49 | TimerApiHelp(t_, 1) 50 | TimerApiHelp(t_, 1) 51 | TimerApiHelp(t_, 1) 52 | TimerApiHelp(t_, 1) 53 | TimerApiHelp(t_, 1) 54 | TimerApiHelp(t_, 1) 55 | TimerApiHelp(t_, 1) 56 | } 57 | -------------------------------------------------------------------------------- /engine/YTool/DebugPrint.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YJson" 5 | ylog "github.com/yxinyi/YCServer/engine/YLog" 6 | ) 7 | 8 | func JsonPrint(args_ ...interface{}) { 9 | for _, _arg_it := range args_ { 10 | ylog.Info("\n%s", YJson.GetPrintStr(_arg_it)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /engine/YTool/position.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type PositionXY struct { 9 | M_x float64 10 | M_y float64 11 | } 12 | 13 | func NewPositionXY() *PositionXY { 14 | return &PositionXY{} 15 | } 16 | func ClonePositionXY(src_ *PositionXY) *PositionXY { 17 | return &PositionXY{ 18 | M_x: src_.M_x, 19 | M_y: src_.M_y, 20 | } 21 | } 22 | 23 | func (xy PositionXY) DebugString() string { 24 | return fmt.Sprintf("M_x:%02f,M_y:%02f", xy.M_x, xy.M_y) 25 | } 26 | 27 | func (xy *PositionXY) GetOffset(rhs PositionXY) *PositionXY { 28 | offset := NewPositionXY() 29 | offset.M_x = rhs.M_x - xy.M_x 30 | offset.M_y = rhs.M_y - xy.M_y 31 | return offset 32 | } 33 | 34 | func (xy *PositionXY) Distance(rhs *PositionXY) float64 { 35 | 36 | xMinusAbs := math.Abs(xy.M_x - rhs.M_x) 37 | yMinusAbs := math.Abs(xy.M_y - rhs.M_y) 38 | 39 | distance := math.Sqrt(xMinusAbs*xMinusAbs + yMinusAbs*yMinusAbs) 40 | 41 | return distance 42 | } 43 | 44 | const PositionXYMIN = 0.001 45 | 46 | func (xy *PositionXY) IsEqual(rhs *PositionXY) bool { 47 | if xy.M_x > rhs.M_x { 48 | if xy.M_x-rhs.M_x > PositionXYMIN { 49 | return false 50 | } 51 | } else { 52 | if rhs.M_x-xy.M_x > PositionXYMIN { 53 | return false 54 | } 55 | } 56 | if xy.M_y > rhs.M_y { 57 | if xy.M_y-rhs.M_y > PositionXYMIN { 58 | return false 59 | } 60 | } else { 61 | if rhs.M_y-xy.M_y > PositionXYMIN { 62 | return false 63 | } 64 | } 65 | return true 66 | } 67 | 68 | func (p *PositionXY) IsSame(rhs_ PositionXY) bool { 69 | if math.Abs(p.M_x-rhs_.M_x) > 0.0001 { 70 | return false 71 | } 72 | if math.Abs(p.M_y-rhs_.M_y) > 0.0001 { 73 | return false 74 | } 75 | return true 76 | } 77 | 78 | func (xy *PositionXY) Offset(offsetPoint *PositionXY) { 79 | xy.M_x += offsetPoint.M_x 80 | xy.M_y += offsetPoint.M_y 81 | } 82 | 83 | func (xy *PositionXY) Clear() { 84 | xy.M_y = 0 85 | xy.M_x = 0 86 | } 87 | func (xy *PositionXY) Clone() *PositionXY { 88 | pos := NewPositionXY() 89 | pos.M_x = xy.M_x 90 | pos.M_y = xy.M_y 91 | return pos 92 | } 93 | func (xy *PositionXY) CopyOther(rhs *PositionXY) { 94 | xy.M_y = rhs.M_y 95 | xy.M_x = rhs.M_x 96 | } 97 | -------------------------------------------------------------------------------- /engine/YTool/queue.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki. 3 | Using this instead of other, simpler, queue implementations (slice+append or linked list) provides 4 | substantial memory and time benefits, and fewer GC pauses. 5 | 6 | The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe. 7 | */ 8 | package YTool 9 | 10 | // minQueueLen is smallest capacity that queue may have. 11 | // Must be power of 2 for bitwise modulus: x % n == x & (n - 1). 12 | const minQueueLen = 16 13 | 14 | // Queue represents a single instance of the queue data structure. 15 | type Queue struct { 16 | buf []interface{} 17 | head, tail, count int 18 | } 19 | 20 | // New constructs and returns a new Queue. 21 | func NewQueue() *Queue { 22 | return &Queue{ 23 | buf: make([]interface{}, minQueueLen), 24 | } 25 | } 26 | 27 | // Length returns the number of elements currently stored in the queue. 28 | func (q *Queue) Length() int { 29 | return q.count 30 | } 31 | 32 | // resizes the queue to fit exactly twice its current contents 33 | // this can result in shrinking if the queue is less than half-full 34 | func (q *Queue) resize() { 35 | newBuf := make([]interface{}, q.count<<1) 36 | 37 | if q.tail > q.head { 38 | copy(newBuf, q.buf[q.head:q.tail]) 39 | } else { 40 | n := copy(newBuf, q.buf[q.head:]) 41 | copy(newBuf[n:], q.buf[:q.tail]) 42 | } 43 | 44 | q.head = 0 45 | q.tail = q.count 46 | q.buf = newBuf 47 | } 48 | 49 | // Add puts an element on the end of the queue. 50 | func (q *Queue) Add(elem interface{}) { 51 | if q.count == len(q.buf) { 52 | q.resize() 53 | } 54 | 55 | q.buf[q.tail] = elem 56 | // bitwise modulus 57 | q.tail = (q.tail + 1) & (len(q.buf) - 1) 58 | q.count++ 59 | } 60 | 61 | // Peek returns the element at the head of the queue. This call panics 62 | // if the queue is empty. 63 | func (q *Queue) Peek() interface{} { 64 | if q.count <= 0 { 65 | return nil 66 | } 67 | 68 | return q.buf[q.head] 69 | } 70 | 71 | // Get returns the element at index i in the queue. If the index is 72 | // invalid, the call will panic. This method accepts both positive and 73 | // negative index values. Index 0 refers to the first element, and 74 | // index -1 refers to the last. 75 | func (q *Queue) Get(i int) interface{} { 76 | // If indexing backwards, convert to positive index. 77 | if i < 0 { 78 | i += q.count 79 | } 80 | if i < 0 || i >= q.count { 81 | return nil 82 | } 83 | // bitwise modulus 84 | return q.buf[(q.head+i)&(len(q.buf)-1)] 85 | } 86 | 87 | // Remove removes and returns the element from the front of the queue. If the 88 | // queue is empty, the call will panic. 89 | func (q *Queue) Pop() interface{} { 90 | if q.count <= 0 { 91 | return nil 92 | } 93 | 94 | ret := q.buf[q.head] 95 | q.buf[q.head] = nil 96 | // bitwise modulus 97 | q.head = (q.head + 1) & (len(q.buf) - 1) 98 | q.count-- 99 | // Resize down if buffer 1/4 full. 100 | if len(q.buf) > minQueueLen && (q.count<<2) == len(q.buf) { 101 | q.resize() 102 | } 103 | return ret 104 | } 105 | -------------------------------------------------------------------------------- /engine/YTool/reactangle.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | ) 6 | 7 | type Rectangle struct { 8 | LeftUp *PositionXY 9 | LeftDown *PositionXY 10 | RightUp *PositionXY 11 | RightDown *PositionXY 12 | } 13 | 14 | func NewRectangle() *Rectangle { 15 | return &Rectangle{ 16 | LeftUp: NewPositionXY(), 17 | LeftDown: NewPositionXY(), 18 | RightUp: NewPositionXY(), 19 | RightDown: NewPositionXY(), 20 | } 21 | } 22 | 23 | func (rec *Rectangle) InitForLefDownRightUP(leftDown *PositionXY, rightUp *PositionXY) { 24 | rec.LeftUp.M_x = leftDown.M_x 25 | rec.LeftUp.M_y = rightUp.M_y 26 | rec.LeftDown.CopyOther(leftDown) 27 | rec.RightDown.M_y = leftDown.M_y 28 | rec.RightDown.M_x = rightUp.M_x 29 | rec.RightUp.CopyOther(rightUp) 30 | } 31 | func (rec *Rectangle) InitForLefUPRightDown(leftUp *PositionXY, rightDown *PositionXY) { 32 | rec.LeftUp.CopyOther(leftUp) 33 | 34 | rec.LeftDown.M_x = leftUp.M_x 35 | rec.LeftDown.M_y = rightDown.M_y 36 | rec.RightDown.CopyOther(rightDown) 37 | 38 | rec.RightUp.M_y = leftUp.M_y 39 | rec.RightUp.M_x = rightDown.M_x 40 | } 41 | 42 | func (rec *Rectangle) DeBugString() string { 43 | byte, err := jsoniter.Marshal(rec) 44 | if err != nil { 45 | return "" 46 | } 47 | return string(byte) 48 | } 49 | 50 | func (rec *Rectangle) Clone() *Rectangle { 51 | return &Rectangle{ 52 | LeftUp: rec.LeftUp.Clone(), 53 | LeftDown: rec.LeftDown.Clone(), 54 | RightUp: rec.RightUp.Clone(), 55 | RightDown: rec.RightDown.Clone(), 56 | } 57 | } 58 | 59 | func (rec *Rectangle) CopyOther(rhs *Rectangle) { 60 | rec.LeftUp.CopyOther(rhs.LeftUp) 61 | rec.LeftDown.CopyOther(rhs.LeftDown) 62 | rec.RightUp.CopyOther(rhs.RightUp) 63 | rec.RightDown.CopyOther(rhs.RightDown) 64 | } 65 | 66 | func (rec *Rectangle) Offset(offsetPoint *PositionXY) { 67 | rec.LeftUp.Offset(offsetPoint) 68 | rec.LeftDown.Offset(offsetPoint) 69 | rec.RightUp.Offset(offsetPoint) 70 | rec.RightDown.Offset(offsetPoint) 71 | } 72 | 73 | //叉乘 74 | func (rec *Rectangle) GetCross(recPoint1 *PositionXY, recPoint2 *PositionXY, checkPoint *PositionXY) float64 { 75 | return ((recPoint2.M_x - recPoint1.M_x) * (checkPoint.M_y - recPoint1.M_y)) - ((checkPoint.M_x - recPoint1.M_x) * (recPoint2.M_y - recPoint1.M_y)) 76 | } 77 | func (rec *Rectangle) IsInsidePoint(checkPoint *PositionXY) bool { 78 | //如果是一个点就不检查碰撞 79 | if rec.LeftUp.M_y == rec.LeftDown.M_y { 80 | return false 81 | } 82 | return (rec.GetCross(rec.LeftUp, rec.LeftDown, checkPoint)*rec.GetCross(rec.RightDown, rec.RightUp, checkPoint) >= 0) && (rec.GetCross(rec.LeftDown, rec.RightDown, checkPoint)*rec.GetCross(rec.RightUp, rec.LeftUp, checkPoint) >= 0) 83 | } 84 | -------------------------------------------------------------------------------- /engine/YTool/syncqueue.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import "sync" 4 | 5 | type SyncQueue struct { 6 | que *Queue 7 | mutex sync.RWMutex 8 | } 9 | 10 | func (q *SyncQueue) Len() int { 11 | q.mutex.RLock() 12 | defer q.mutex.RUnlock() 13 | 14 | return q.que.Length() 15 | } 16 | 17 | func (q *SyncQueue) Add(elem interface{}) { 18 | q.mutex.Lock() 19 | defer q.mutex.Unlock() 20 | 21 | q.que.Add(elem) 22 | } 23 | 24 | func (q *SyncQueue) Peek() interface{} { 25 | q.mutex.RLock() 26 | defer q.mutex.RUnlock() 27 | 28 | return q.que.Peek() 29 | } 30 | 31 | func (q *SyncQueue) Get(i int) interface{} { 32 | q.mutex.RLock() 33 | defer q.mutex.RUnlock() 34 | 35 | return q.que.Get(i) 36 | } 37 | 38 | func (q *SyncQueue) Pop() interface{} { 39 | 40 | q.mutex.Lock() 41 | defer q.mutex.Unlock() 42 | 43 | return q.que.Pop() 44 | } 45 | 46 | func (q *SyncQueue) RLockRange(f func(interface{})) { 47 | q.mutex.RLock() 48 | defer q.mutex.RUnlock() 49 | 50 | for i := 0; i < q.que.Length(); i++ { 51 | f(q.Get(i)) 52 | } 53 | } 54 | 55 | func NewSyncQueue() *SyncQueue { 56 | syncQueue := SyncQueue{} 57 | syncQueue.que = NewQueue() 58 | 59 | return &syncQueue 60 | } 61 | -------------------------------------------------------------------------------- /engine/YTool/timeter.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type TimerMeterInfo struct { 8 | Name string 9 | Timer int64 10 | } 11 | 12 | type TimerMeter struct { 13 | timerInfoPool map[string]int 14 | timerPool map[int]*TimerMeterInfo 15 | } 16 | 17 | func NewTimerMeter() *TimerMeter { 18 | return &TimerMeter{ 19 | timerInfoPool: make(map[string]int), 20 | timerPool: make(map[int]*TimerMeterInfo), 21 | } 22 | } 23 | func (t *TimerMeter) Start(name string) { 24 | timer := &TimerMeterInfo{ 25 | Name: name, 26 | Timer: time.Now().UnixNano(), 27 | } 28 | _, exists := t.timerInfoPool[name] 29 | if exists { 30 | t.timerPool[t.timerInfoPool[name]] = timer 31 | } else { 32 | idx := len(t.timerPool) 33 | t.timerInfoPool[name] = idx 34 | t.timerPool[idx] = timer 35 | } 36 | } 37 | 38 | func (t *TimerMeter) End(name string) { 39 | idx, exists := t.timerInfoPool[name] 40 | if !exists { 41 | return 42 | } 43 | delete(t.timerInfoPool, name) 44 | t.timerPool[idx].Timer = time.Now().UnixNano() - t.timerPool[idx].Timer 45 | } 46 | 47 | /*func (t *TimerMeter) Print(name string) { 48 | idx, exists := t.timerInfoPool[name] 49 | if !exists { 50 | return 51 | } 52 | 53 | l4g.Info("[TimerMeter] name [%v] time [%04f]", name, t.timerPool[idx].Timer/int64(time.Millisecond)) 54 | } 55 | 56 | func (t *TimerMeter) PrintAll() { 57 | meterList := make([]*TimerMeterInfo, 0) 58 | for idx := 0; idx < len(t.timerPool); idx++ { 59 | meterList = append(meterList, t.timerPool[idx]) 60 | //l4g.Info("[TimerMeter] name [%v] time [%04f]", t.timerPool[idx].Name, float64(t.timerPool[idx].Timer/int64(time.Millisecond))/float64(1000)) 61 | } 62 | sort.Slice(meterList, func(lhs int, rhs int) bool { 63 | return meterList[lhs].Timer > meterList[rhs].Timer 64 | }) 65 | 66 | for _, it := range meterList { 67 | l4g.Info("[TimerMeter] name [%v] time [%04f]", it.Name, float64(it.Timer/int64(time.Millisecond))/float64(1000)) 68 | } 69 | }*/ -------------------------------------------------------------------------------- /engine/YTool/tool.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | "sort" 7 | ) 8 | 9 | func Uint32SetConvertToSortSlice(set_ map[uint32]struct{}) []uint32 { 10 | _ret_slice := make([]uint32, 0, len(set_)) 11 | for _it := range set_ { 12 | _ret_slice = append(_ret_slice, _it) 13 | } 14 | sort.Slice(_ret_slice, func(i_, j_ int) bool { 15 | return _ret_slice[i_] < _ret_slice[j_] 16 | }) 17 | return _ret_slice 18 | } 19 | 20 | func Uint64SetClone(set_ map[uint64]struct{}) map[uint64]struct{} { 21 | _ret_set := make(map[uint64]struct{}) 22 | for _it := range set_ { 23 | _ret_set[_it] = struct{}{} 24 | } 25 | return _ret_set 26 | } 27 | 28 | func Uint64SetMerge(lhs_ map[uint64]struct{}, rhs_ map[uint64]struct{}) map[uint64]struct{} { 29 | for _it := range rhs_ { 30 | lhs_[_it] = struct{}{} 31 | } 32 | return lhs_ 33 | } 34 | 35 | func Uint64MapUint64SetMerge(lhs_ map[uint64]map[uint64]struct{}, rhs_ map[uint64]map[uint64]struct{}) map[uint64]map[uint64]struct{} { 36 | for _key, _set_it := range rhs_ { 37 | _, exists := lhs_[_key] 38 | if !exists { 39 | lhs_[_key] = make(map[uint64]struct{}) 40 | } 41 | lhs_[_key] = Uint64SetMerge(lhs_[_key], _set_it) 42 | } 43 | return lhs_ 44 | } 45 | 46 | func Float64Equal(check_num_, target_ float64) bool { 47 | if math.Abs(check_num_-target_) < 0.00001 { 48 | return true 49 | } 50 | return false 51 | } 52 | 53 | 54 | func GetFuncInTypeList(func_ reflect.Value)[]reflect.Type{ 55 | ret_list := make([]reflect.Type, 0) 56 | for _idx := 0; _idx < func_.Type().NumIn(); _idx++ { 57 | ret_list = append(ret_list, func_.Type().In(_idx)) 58 | } 59 | return ret_list 60 | } 61 | 62 | func GetSetUint32Diff(lhs_ map[uint32]struct{}, rhs_ map[uint32]struct{}) map[uint32]struct{} { 63 | _ret_set := make(map[uint32]struct{}) 64 | for _it := range lhs_{ 65 | _ret_set[_it] = struct{}{} 66 | } 67 | for _it := range rhs_ { 68 | delete(_ret_set, _it) 69 | } 70 | return _ret_set 71 | } 72 | func GetSetUint64Diff(lhs_ map[uint64]struct{}, rhs_ map[uint64]struct{}) map[uint64]struct{} { 73 | _ret_set := make(map[uint64]struct{}) 74 | for _it := range lhs_{ 75 | _ret_set[_it] = struct{}{} 76 | } 77 | for _it := range rhs_ { 78 | delete(_ret_set, _it) 79 | } 80 | return _ret_set 81 | } -------------------------------------------------------------------------------- /engine/YTool/tools_test.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | 8 | func TestUint64SetMerge(t_ *testing.T) { 9 | _lhs := map[uint64]struct{}{0: {},1:{},2:{},3:{}} 10 | _rhs := map[uint64]struct{}{3: {},4:{},5:{},6:{}} 11 | 12 | _final := Uint64SetMerge(_lhs,_rhs) 13 | t_.Logf("_lhs[%v]",_lhs) 14 | t_.Logf("_rhs[%v]",_rhs) 15 | t_.Logf("_final[%v]",_final) 16 | } 17 | -------------------------------------------------------------------------------- /engine/YTool/uidFactory.go: -------------------------------------------------------------------------------- 1 | package YTool 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func init() { 9 | rand.Seed(time.Now().Unix()) 10 | } 11 | 12 | func BuildUIDUint64()uint64{ 13 | return rand.Uint64() 14 | } -------------------------------------------------------------------------------- /examples/AoiAstarExample/Client/Client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hajimehoshi/ebiten/v2" 6 | "github.com/yxinyi/YCServer/engine/YNet" 7 | "github.com/yxinyi/YCServer/engine/YTool" 8 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 9 | "log" 10 | ) 11 | 12 | var g_client_cnn = YNet.NewConnect() 13 | var g_sync_queue = YTool.NewSyncQueue() 14 | 15 | func main() { 16 | fmt.Println("Client start") 17 | g_client_cnn.Connect("127.0.0.1:20000") 18 | g_client_cnn.Start() 19 | g_client_cnn.SendJson(Msg.C2S_Login{}) 20 | g_client_cnn.SendJson(Msg.C2S_FirstEnterMap{}) 21 | game, err := NewMainGame() 22 | if err != nil { 23 | log.Fatal(err.Error()) 24 | } 25 | ebiten.SetWindowSize(ScreenWidth, ScreenHeight) 26 | ebiten.SetWindowTitle("mmo aoi test") 27 | if err := ebiten.RunGame(game); err != nil { 28 | log.Fatal(err.Error()) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Client/Game.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hajimehoshi/ebiten/v2" 5 | "github.com/yxinyi/YCServer/engine/YNet" 6 | "image/color" 7 | "math/rand" 8 | "time" 9 | ) 10 | 11 | func init() { 12 | rand.Seed(time.Now().UnixNano()) 13 | g_map.Init() 14 | } 15 | 16 | // Game represents a game state. 17 | type Game struct { 18 | } 19 | 20 | // NewGame generates a new Game object. 21 | func NewMainGame() (*Game, error) { 22 | g := &Game{ 23 | } 24 | var err error 25 | if err != nil { 26 | return nil, err 27 | } 28 | return g, nil 29 | } 30 | 31 | // Layout implements ebiten.Game's Layout. 32 | func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { 33 | return ScreenWidth, ScreenHeight 34 | } 35 | 36 | // Update updates the current game state. 37 | func (g *Game) Update() error { 38 | 39 | for more := true; more; { 40 | select { 41 | case _net_msg := <-YNet.G_net_msg_chan: 42 | YNet.Dispatch(_net_msg.M_session, _net_msg.M_net_msg) 43 | default: 44 | more = false 45 | } 46 | } 47 | g_map.Update() 48 | return nil 49 | } 50 | 51 | // Draw draws the current game to the given screen. 52 | func (g *Game) Draw(screen *ebiten.Image) { 53 | screen.Fill(color.NRGBA{0x00, 0x40, 0x80, 0xff}) 54 | g_map.Draw(screen) 55 | } 56 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Client/Map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hajimehoshi/ebiten/v2" 6 | "github.com/hajimehoshi/ebiten/v2/ebitenutil" 7 | "github.com/hajimehoshi/ebiten/v2/inpututil" 8 | "github.com/yxinyi/YCServer/engine/YNet" 9 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 10 | "golang.org/x/image/font" 11 | "golang.org/x/image/font/gofont/goregular" 12 | "golang.org/x/image/font/opentype" 13 | "image/color" 14 | "log" 15 | "math" 16 | ) 17 | 18 | const ( 19 | ScreenWidth = 1280 20 | ScreenHeight = 720 21 | gridSize = 10 22 | userGridSize = 5 23 | ) 24 | 25 | var g_center_pos = Msg.PositionXY{float64(ScreenWidth / 2), float64(ScreenHeight / 2)} 26 | 27 | var uiFont font.Face 28 | 29 | type Map struct { 30 | m_user_list map[uint64]Msg.UserData 31 | } 32 | 33 | func NewMap() *Map { 34 | return &Map{ 35 | m_user_list: make(map[uint64]Msg.UserData), 36 | } 37 | } 38 | 39 | var g_map = NewMap() 40 | var g_main_uid uint64 41 | var g_main_path_node []Msg.PositionXY 42 | var g_main_check_node []Msg.PositionXY 43 | var g_map_maze_info Msg.S2C_FirstEnterMap 44 | 45 | func (m *Map) Init() { 46 | tt, err := opentype.Parse(goregular.TTF) 47 | if err != nil { 48 | log.Fatal(err.Error()) 49 | } 50 | uiFont, _ = opentype.NewFace(tt, &opentype.FaceOptions{ 51 | Size: 12, 52 | DPI: 72, 53 | Hinting: font.HintingFull, 54 | }) 55 | ebiten.SetMaxTPS(30) 56 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2C_FirstEnterMap) { 57 | g_map_maze_info = msg_ 58 | m.UpdateUser(msg_.M_data) 59 | }) 60 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2C_Login) { 61 | g_main_uid = msg_.M_data.M_uid 62 | m.AddNewUser(msg_.M_data) 63 | }) 64 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2C_MapAStarNodeUpdate) { 65 | g_main_path_node = msg_.M_path 66 | }) 67 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2CMapAddUser) { 68 | for _, _it := range msg_.M_user { 69 | m.AddNewUser(_it) 70 | } 71 | 72 | }) 73 | /* _msg_count := int32(0) 74 | go func() { 75 | _ticker := time.NewTicker(time.Second) 76 | for { 77 | select { 78 | case <-_ticker.C: 79 | fmt.Printf("msg count [%v]\n", atomic.LoadInt32(&_msg_count)) 80 | atomic.StoreInt32(&_msg_count, 0) 81 | } 82 | } 83 | 84 | }()*/ 85 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2CMapUpdateUser) { 86 | //atomic.AddInt32(&_msg_count, 1) 87 | for _, _it := range msg_.M_user { 88 | m.UpdateUser(_it) 89 | } 90 | }) 91 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2CMapDeleteUser, ) { 92 | for _, _it := range msg_.M_user { 93 | m.DeleteUser(_it.M_uid) 94 | } 95 | }) 96 | 97 | /* YNet.Register(Msg.MsgID_S2CUserMove, m.UserMove) 98 | YNet.Register(Msg.MSG_S2C_MAP_FULL_SYNC, func(msg_ Msg.S2CMapFullSync, _ YNet.Session) { 99 | for _, _it := range msg_.M_user { 100 | m.AddNewUser(_it) 101 | } 102 | }) 103 | YNet.Register(Msg.MSG_S2C_MAP_ADD_USER, func(msg_ Msg.S2CMapAddUser, _ YNet.Session) { 104 | for _, _it := range msg_.M_user { 105 | m.AddNewUser(_it) 106 | } 107 | 108 | }) 109 | YNet.Register(Msg.MSG_S2C_MAP_UPDATE_USER, func(msg_ Msg.S2CMapUpdateUser, _ YNet.Session) { 110 | for _, _it := range msg_.M_user { 111 | m.UpdateUser(_it) 112 | } 113 | }) 114 | YNet.Register(Msg.MSG_S2C_MAP_DELETE_USER, func(msg_ Msg.S2CMapDeleteUser, _ YNet.Session) { 115 | for _, _it := range msg_.M_user { 116 | m.DeleteUser(_it.M_module_uid) 117 | } 118 | }) 119 | YNet.Register(Msg.MsgID_S2CUserSuccessLogin, func(msg_ Msg.S2CUserSuccessLogin, _ YNet.Session) { 120 | g_main_uid = msg_.M_defalut_value.M_module_uid 121 | m.AddNewUser(msg_.M_defalut_value) 122 | }) 123 | YNet.Register(Msg.MSG_S2C_MAP_ASTAR_NODE_UPDATE, func(msg_ Msg.S2C_MapAStarNodeUpdate, _ YNet.Session) { 124 | g_main_path_node = msg_.M_path 125 | }) 126 | YNet.Register(Msg.MSG_S2C_MAP_FLUSH_MAP_MAZE, func(msg_ Msg.S2CFlushMapMaze, _ YNet.Session) { 127 | g_map_maze_info = msg_ 128 | })*/ 129 | } 130 | func (m *Map) DeleteUser(uid_ uint64) { 131 | delete(m.m_user_list, uid_) 132 | } 133 | 134 | func (m *Map) AddNewUser(user_data_ Msg.UserData) { 135 | m.m_user_list[user_data_.M_uid] = user_data_ 136 | } 137 | 138 | var g_slope string 139 | 140 | func (m *Map) UpdateUser(user_data_ Msg.UserData) { 141 | 142 | if g_main_uid == user_data_.M_uid { 143 | g_slope = fmt.Sprintf("%.2f", (user_data_.M_pos.M_y-m.m_user_list[user_data_.M_uid].M_pos.M_y)/(user_data_.M_pos.M_x-m.m_user_list[user_data_.M_uid].M_pos.M_x)) 144 | } 145 | m.m_user_list[user_data_.M_uid] = user_data_ 146 | } 147 | 148 | func (m *Map) UserMove(msg_ Msg.S2C_MOVE, _ YNet.Session) { 149 | m.m_user_list[msg_.M_uid] = msg_.M_data 150 | } 151 | 152 | func (m *Map) MainPos() Msg.PositionXY { 153 | return m.m_user_list[g_main_uid].M_pos 154 | } 155 | 156 | func (m *Map) Update() { 157 | if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { 158 | _tar_x, _tar_y := ebiten.CursorPosition() 159 | _x_diff := float64(_tar_x) - g_center_pos.M_x 160 | _y_diff := float64(_tar_y) - g_center_pos.M_y 161 | 162 | //_touch_pos := m.PosConvert(Msg.PositionXY{ float64(_tar_x), float64(_tar_y)}) 163 | g_client_cnn.SendJson(Msg.C2S_UserMove{ 164 | Msg.PositionXY{ 165 | m.MainPos().M_x + _x_diff, 166 | m.MainPos().M_y + _y_diff, 167 | }, 168 | }) 169 | } 170 | 171 | if inpututil.IsKeyJustPressed(ebiten.KeyT) { 172 | switch ebiten.MaxTPS() { 173 | case 30: 174 | ebiten.SetMaxTPS(60) 175 | case 60: 176 | ebiten.SetMaxTPS(90) 177 | case 90: 178 | ebiten.SetMaxTPS(120) 179 | case 120: 180 | ebiten.SetMaxTPS(150) 181 | case 150: 182 | ebiten.SetMaxTPS(30) 183 | } 184 | 185 | } 186 | } 187 | 188 | func (m *Map) InViewRange(pos Msg.PositionXY) bool { 189 | _distance := pos.DistancePosition(m.MainPos()) 190 | if math.Abs( _distance.M_x) > ScreenWidth/2-10 || math.Abs(_distance.M_y) > ScreenHeight/2-10{ 191 | return false 192 | } 193 | return true 194 | } 195 | func (m *Map) PosConvert(pos Msg.PositionXY) Msg.PositionXY { 196 | 197 | _main_user_pos := m.MainPos() 198 | 199 | _x_diff := g_center_pos.M_x - _main_user_pos.M_x 200 | _y_diff := g_center_pos.M_y - _main_user_pos.M_y 201 | pos.M_x += _x_diff 202 | pos.M_y += _y_diff 203 | return pos 204 | } 205 | 206 | func (m *Map) Draw(screen *ebiten.Image) { 207 | //text.Draw(screen, g_slope, uiFont, int(100), int(100), color.White) 208 | 209 | _grid_size := g_map_maze_info.M_height / float64(len(g_map_maze_info.M_maze)) 210 | for _row_idx_it, _row_it := range g_map_maze_info.M_maze { 211 | _row_idx := _row_idx_it 212 | for _col_idx_it, _block_val := range _row_it { 213 | _col_idx := _col_idx_it 214 | if _block_val != 0 { 215 | _block_pos :=Msg.PositionXY{float64(_col_idx) * _grid_size, float64(_row_idx) * _grid_size} 216 | if !m.InViewRange(_block_pos){ 217 | continue 218 | } 219 | _block_pos = m.PosConvert(_block_pos) 220 | ebitenutil.DrawRect(screen, _block_pos.M_x, _block_pos.M_y, _grid_size, _grid_size, color.Black) 221 | } /* else { 222 | ebitenutil.DrawRect(screen, float64(_col_idx)*_grid_size, float64(_row_idx)*_grid_size, _grid_size, _grid_size, color.White) 223 | }*/ 224 | /*detailStr := fmt.Sprintf("%d", _row_idx*len(g_map_maze_info.M_maze[0])+_col_idx) 225 | text.Draw(screen, detailStr, uiFont, int (float64(_col_idx)*_grid_size + _grid_size/2), int (float64(_row_idx)*_grid_size+ _grid_size/2), color.White) */ 226 | } 227 | } 228 | 229 | for _, it := range m.m_user_list { 230 | for _, path_it := range it.M_path { 231 | if !m.InViewRange(path_it){ 232 | continue 233 | } 234 | _path_pos := m.PosConvert(path_it) 235 | ebitenutil.DrawRect(screen, _path_pos.M_x, _path_pos.M_y, gridSize, gridSize, color.RGBA{0xff, 0x00, 0x00, 0xff}) 236 | } 237 | } 238 | 239 | for _, path_it := range g_main_path_node { 240 | if !m.InViewRange(path_it){ 241 | continue 242 | } 243 | _path_pos := m.PosConvert(path_it) 244 | ebitenutil.DrawRect(screen, _path_pos.M_x, _path_pos.M_y, gridSize, gridSize, color.RGBA{0xff, 0x00, 0x00, 0xff}) 245 | } 246 | 247 | for _uid_it, it := range m.m_user_list { 248 | if !m.InViewRange(it.M_pos){ 249 | continue 250 | } 251 | if m.m_user_list[_uid_it].M_pos.Distance(it.M_pos) > 100 { 252 | panic("1") 253 | } 254 | 255 | if g_main_uid == _uid_it { 256 | //detailStr := fmt.Sprintf("%.2f,%.2f", it.M_pos.M_x, it.M_pos.M_y) 257 | //text.Draw(screen, detailStr, uiFont, int(it.M_pos.M_x), int(it.M_pos.M_y+20), color.White) 258 | _main_user := m.PosConvert(Msg.PositionXY{it.M_pos.M_x + (gridSize-userGridSize)/2, it.M_pos.M_y + (gridSize-userGridSize)/2}) 259 | ebitenutil.DrawRect(screen, _main_user.M_x, _main_user.M_y, userGridSize, userGridSize, color.RGBA{0xff, 0xa0, 0x00, 0xff}) 260 | } else { 261 | //detailStr := fmt.Sprintf("%.2f,%.2f", it.M_pos.M_x, it.M_pos.M_y) 262 | //text.Draw(screen, detailStr, uiFont, int(it.M_pos.M_x), int(it.M_pos.M_y+20), color.White) 263 | _main_user := m.PosConvert(Msg.PositionXY{it.M_pos.M_x + (gridSize-userGridSize)/2, it.M_pos.M_y + (gridSize-userGridSize)/2}) 264 | ebitenutil.DrawRect(screen, _main_user.M_x, _main_user.M_y, userGridSize, userGridSize, color.RGBA{0x80, 0xa0, 0x00, 0xff}) 265 | //ebitenutil.DrawRect(screen, it.M_pos.M_x+(gridSize-userGridSize)/2, it.M_pos.M_y+(gridSize-userGridSize)/2, userGridSize, userGridSize, color.RGBA{0x80, 0xa0, 0xc0, 0xff}) 266 | } 267 | } 268 | 269 | ebitenutil.DebugPrint(screen, fmt.Sprintf("MAX: %d\nTPS: %0.2f\nFPS: %0.2f", ebiten.MaxTPS(), ebiten.CurrentTPS(), ebiten.CurrentFPS())) 270 | } 271 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Msg/msg.go: -------------------------------------------------------------------------------- 1 | package Msg 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | type Message struct { 9 | Id int 10 | Number int 11 | } 12 | 13 | type UserData struct { 14 | M_uid uint64 15 | M_pos PositionXY 16 | M_path []PositionXY 17 | } 18 | 19 | type PositionXY struct { 20 | M_x float64 21 | M_y float64 22 | } 23 | 24 | func (p PositionXY) DebugString() string { 25 | return fmt.Sprintf("[x:%v|y:%v]", p.M_x, p.M_y) 26 | } 27 | 28 | func (p *PositionXY) IsSame(rhs_ PositionXY) bool { 29 | if math.Abs(p.M_x-rhs_.M_x) > 0.0001 { 30 | return false 31 | } 32 | if math.Abs(p.M_y-rhs_.M_y) > 0.0001 { 33 | return false 34 | } 35 | return true 36 | } 37 | 38 | func (p *PositionXY) DistancePosition(rhs_ PositionXY) *PositionXY { 39 | _pos := &PositionXY{} 40 | _pos.M_x = rhs_.M_x - p.M_x 41 | _pos.M_y = rhs_.M_y - p.M_y 42 | return _pos 43 | } 44 | 45 | func (p PositionXY) Distance(rhs_ PositionXY) float64 { 46 | _dx := math.Abs(p.M_x - rhs_.M_x) 47 | _dy := math.Abs(p.M_y - rhs_.M_y) 48 | return math.Sqrt(_dx*_dx + _dy*_dy) 49 | } 50 | 51 | type C2SUserMove struct { 52 | M_pos PositionXY 53 | } 54 | 55 | type S2C_MOVE struct { 56 | M_uid uint64 57 | M_data UserData 58 | } 59 | 60 | type S2CMapFullSync struct { 61 | M_user []UserData 62 | } 63 | 64 | type S2CMapAddUser struct { 65 | M_user []UserData 66 | } 67 | type S2CMapUpdateUser struct { 68 | M_user []UserData 69 | } 70 | type S2CMapDeleteUser struct { 71 | M_user []UserData 72 | } 73 | type S2C_MapAStarNodeUpdate struct { 74 | M_uid uint64 75 | M_path []PositionXY 76 | } 77 | 78 | type C2S_Login struct { 79 | 80 | } 81 | 82 | type S2C_Login struct { 83 | M_data UserData 84 | } 85 | 86 | type C2S_FirstEnterMap struct { 87 | 88 | } 89 | 90 | type S2C_FirstEnterMap struct { 91 | M_map_uid uint64 92 | M_maze [][]float64 93 | M_height float64 94 | M_width float64 95 | M_data UserData 96 | } 97 | 98 | 99 | type C2S_UserMove struct { 100 | M_pos PositionXY 101 | } 102 | 103 | 104 | type MapLoad struct { 105 | M_map_uid uint64 106 | M_load uint32 107 | } 108 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Logic/Aoi/Aoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | type AoiCell struct { 4 | m_watch_list map[uint64]map[uint64]struct{} 5 | M_enter_callback AoiEnterCallBack 6 | M_quit_callback AoiQuitCallBack 7 | M_move_callback AoiMoveCallBack 8 | M_add_watch_callback AoiAddWatch 9 | } 10 | 11 | func NewAoiCell() *AoiCell { 12 | _cell := &AoiCell{ 13 | m_watch_list: make(map[uint64]map[uint64]struct{}), 14 | } 15 | 16 | return _cell 17 | } 18 | 19 | func (cell *AoiCell) enterCell(enter_ uint64) { 20 | cell.m_watch_list[enter_] = make(map[uint64]struct{}) 21 | } 22 | func (cell *AoiCell) notifyEnterCell(enter_ uint64) { 23 | for _it := range cell.m_watch_list { 24 | cell.M_enter_callback(_it, enter_) 25 | cell.M_enter_callback(enter_,_it) 26 | } 27 | } 28 | 29 | func (cell *AoiCell) quitCell(quit uint64) { 30 | delete(cell.m_watch_list, quit) 31 | } 32 | func (cell *AoiCell) notifyQuitCell(quit_ uint64) { 33 | for _it := range cell.m_watch_list { 34 | cell.M_quit_callback(_it, quit_) 35 | cell.M_quit_callback(quit_, _it) 36 | } 37 | } 38 | 39 | func (cell *AoiCell) updateCell(enter_ uint64) { 40 | for _it := range cell.m_watch_list { 41 | cell.M_move_callback(_it, enter_) 42 | cell.M_move_callback(enter_,_it) 43 | } 44 | } 45 | 46 | /*func (cell *AoiCell) enterCell(enter_ uint32) { 47 | cell.m_watch_list[enter_] = make(map[uint32]struct{}) 48 | for _it := range cell.m_watch_list { 49 | if cell.M_add_watch_callback(enter_, _it) { 50 | cell.m_watch_list[enter_][_it] = struct{}{} 51 | cell.M_enter_callback(enter_, _it) 52 | } 53 | if cell.M_add_watch_callback(_it, enter_) { 54 | cell.m_watch_list[_it][enter_] = struct{}{} 55 | cell.M_enter_callback(_it, enter_) 56 | } 57 | } 58 | } 59 | 60 | func (cell *AoiCell) quitCell(enter_ uint32) { 61 | _watch_list := cell.m_watch_list[enter_] 62 | for _it := range _watch_list { 63 | if enter_== _it { 64 | delete(cell.m_watch_list[_it], enter_) 65 | continue 66 | } 67 | cell.M_quit_callback(enter_, _it) 68 | _, exists := cell.m_watch_list[_it][enter_] 69 | if exists { 70 | cell.M_quit_callback(_it, enter_) 71 | } 72 | delete(cell.m_watch_list[_it], enter_) 73 | } 74 | delete(cell.m_watch_list, enter_) 75 | } 76 | 77 | func (cell *AoiCell) updateCell(enter_ uint32) { 78 | for _it := range cell.m_watch_list { 79 | if cell.M_add_watch_callback(enter_, _it) { 80 | _, exists := cell.m_watch_list[enter_][_it] 81 | if exists { 82 | cell.M_move_callback(enter_, _it) 83 | } else { 84 | cell.m_watch_list[enter_][_it] = struct{}{} 85 | cell.M_enter_callback(enter_, _it) 86 | } 87 | 88 | } else { 89 | _, exists := cell.m_watch_list[enter_][_it] 90 | if exists { 91 | cell.M_quit_callback(enter_, _it) 92 | delete(cell.m_watch_list[enter_], _it) 93 | } 94 | } 95 | 96 | if cell.M_add_watch_callback(_it, enter_) { 97 | _, exists := cell.m_watch_list[_it][enter_] 98 | if exists { 99 | cell.M_move_callback(_it, enter_) 100 | } else { 101 | cell.m_watch_list[_it][enter_] = struct{}{} 102 | cell.M_enter_callback(_it, enter_) 103 | } 104 | } else { 105 | _, exists := cell.m_watch_list[_it][enter_] 106 | if exists { 107 | cell.M_quit_callback(_it, enter_) 108 | delete(cell.m_watch_list[_it], enter_) 109 | } 110 | } 111 | 112 | } 113 | }*/ 114 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Logic/Aoi/AoiManager.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 5 | ) 6 | 7 | type AoiMoveCallBack func(move_, tar_ uint64) 8 | type AoiEnterCallBack func(move_, tar_ uint64) 9 | type AoiQuitCallBack func(move_, tar_ uint64) 10 | type AoiAddWatch func(move_, tar_ uint64) bool 11 | 12 | type AoiManager struct { 13 | M_height float64 14 | M_width float64 15 | m_aoi_list map[uint32]*AoiCell 16 | M_current_index map[uint64]uint32 17 | m_block_height float64 18 | m_block_width float64 19 | m_block_size float64 20 | } 21 | 22 | func NewAoiManager(width_, height_, block_size_ float64) *AoiManager { 23 | _mgr := &AoiManager{ 24 | m_aoi_list: make(map[uint32]*AoiCell), 25 | M_current_index: make(map[uint64]uint32), 26 | } 27 | _mgr.M_height = height_ 28 | _mgr.M_width = width_ 29 | _mgr.m_block_height = height_ / block_size_ 30 | _mgr.m_block_width = width_ / block_size_ 31 | _mgr.m_block_size = block_size_ 32 | return _mgr 33 | } 34 | 35 | func (mgr *AoiManager) Init(add_watch_call_ AoiAddWatch, move_call_ AoiMoveCallBack, enter_call_ AoiEnterCallBack, quit_call_ AoiQuitCallBack) { 36 | for _row_idx := uint32(0); _row_idx < uint32(mgr.m_block_size); _row_idx++ { 37 | for _col_idx := uint32(0); _col_idx < uint32(mgr.m_block_size); _col_idx++ { 38 | _cell := NewAoiCell() 39 | _cell.M_move_callback = move_call_ 40 | _cell.M_enter_callback = enter_call_ 41 | _cell.M_quit_callback = quit_call_ 42 | _cell.M_add_watch_callback = add_watch_call_ 43 | mgr.m_aoi_list[mgr.buildIndex(_row_idx, _col_idx)] = _cell 44 | } 45 | } 46 | } 47 | 48 | func getDiff(lhs_ map[uint32]struct{}, rhs_ map[uint32]struct{}) map[uint32]struct{} { 49 | _ret := make(map[uint32]struct{}) 50 | for _it := range lhs_ { 51 | _ret[_it] = struct{}{} 52 | } 53 | for _it := range rhs_ { 54 | delete(_ret, _it) 55 | } 56 | 57 | return _ret 58 | } 59 | 60 | func (mgr *AoiManager) Enter(enter_ uint64, pos_ Msg.PositionXY) { 61 | _current_index := mgr.CalcIndex(pos_) 62 | _cell := mgr.m_aoi_list[_current_index] 63 | _cell.enterCell(enter_) 64 | _round_arr := mgr.getRoundBlock(_current_index) 65 | for _it := range _round_arr { 66 | _cell, exists := mgr.m_aoi_list[_it] 67 | if exists { 68 | _cell.notifyEnterCell(enter_) 69 | } 70 | } 71 | mgr.M_current_index[enter_] = _current_index 72 | } 73 | 74 | func (mgr *AoiManager) Quit(quit_ uint64, pos_ Msg.PositionXY) { 75 | _current_index := mgr.CalcIndex(pos_) 76 | _cell := mgr.m_aoi_list[_current_index] 77 | _cell.quitCell(quit_) 78 | _round_arr := mgr.getRoundBlock(_current_index) 79 | for _it := range _round_arr { 80 | _cell, exists := mgr.m_aoi_list[_it] 81 | if exists { 82 | _cell.notifyQuitCell(quit_) 83 | } 84 | } 85 | delete(mgr.M_current_index, quit_) 86 | } 87 | 88 | func (mgr *AoiManager) Move(move_ uint64, pos_ Msg.PositionXY) { 89 | 90 | _old_round_arr := mgr.getOldRoundBlock(move_) 91 | 92 | _current_index := mgr.CalcIndex(pos_) 93 | _new_round_arr := mgr.getRoundBlock(_current_index) 94 | 95 | if _current_index != mgr.M_current_index[move_] { 96 | _enter_cell := mgr.m_aoi_list[_current_index] 97 | _enter_cell.enterCell(move_) 98 | 99 | } 100 | _enter_cell := getDiff(_new_round_arr, _old_round_arr) 101 | for _it := range _enter_cell { 102 | _cell, exists := mgr.m_aoi_list[_it] 103 | if exists { 104 | _cell.notifyEnterCell(move_) 105 | } 106 | } 107 | 108 | _update_cell := getDiff(_new_round_arr, _enter_cell) 109 | for _it := range _update_cell { 110 | _cell, exists := mgr.m_aoi_list[_it] 111 | if exists { 112 | _cell.updateCell(move_) 113 | } 114 | } 115 | if _current_index != mgr.M_current_index[move_] { 116 | _quit_cell := mgr.m_aoi_list[mgr.M_current_index[move_]] 117 | _quit_cell.quitCell(move_) 118 | } 119 | _quit_cell := getDiff(_old_round_arr, _new_round_arr) 120 | for _it := range _quit_cell { 121 | _cell, exists := mgr.m_aoi_list[_it] 122 | if exists { 123 | _cell.notifyQuitCell(move_) 124 | } 125 | } 126 | mgr.M_current_index[move_] = _current_index 127 | 128 | } 129 | 130 | func (mgr *AoiManager) CalcIndex(xy_ Msg.PositionXY) uint32 { 131 | return mgr.buildIndex(uint32(xy_.M_x/mgr.m_block_width), uint32(xy_.M_y/mgr.m_block_height)) 132 | } 133 | 134 | func (mgr *AoiManager) buildIndex(row_, col_ uint32) uint32 { 135 | return row_ + col_*uint32(mgr.m_block_size) 136 | } 137 | 138 | func (mgr *AoiManager) getOldRoundBlock(uid_ uint64) map[uint32]struct{} { 139 | _old_index := mgr.M_current_index[uid_] 140 | return mgr.getRoundBlock(_old_index) 141 | } 142 | 143 | func (mgr *AoiManager) getRoundBlock(cent_index_ uint32) map[uint32]struct{} { 144 | _ret_round := make(map[uint32]struct{}) 145 | _cent_idex := int(cent_index_) 146 | _block_size := int(mgr.m_block_size) 147 | 148 | _max_idx := int(mgr.m_block_size * mgr.m_block_size) 149 | 150 | _cent_row := int(cent_index_ / uint32(mgr.m_block_size)) 151 | _ret_round[cent_index_] = struct{}{} 152 | { 153 | _left_up := _cent_idex - _block_size - 1 154 | if _left_up >= 0 && (_left_up/_block_size+1) == _cent_row { 155 | _ret_round[uint32(_left_up)] = struct{}{} 156 | } 157 | } 158 | 159 | { 160 | _up := _cent_idex - _block_size 161 | if _up >= 0 && (_up/_block_size+1) == _cent_row { 162 | _ret_round[uint32(_up)] = struct{}{} 163 | } 164 | } 165 | { 166 | _up_right := _cent_idex - _block_size + 1 167 | if _up_right >= 0 && (_up_right/_block_size+1) == _cent_row { 168 | _ret_round[uint32(_up_right)] = struct{}{} 169 | } 170 | } 171 | 172 | { 173 | _left := _cent_idex - 1 174 | if _left >= 0 && (_left/_block_size) == _cent_row { 175 | _ret_round[uint32(_left)] = struct{}{} 176 | } 177 | } 178 | { 179 | _right := _cent_idex + 1 180 | if _right >= 0 && (_right/_block_size) == _cent_row { 181 | _ret_round[uint32(_right)] = struct{}{} 182 | } 183 | } 184 | 185 | { 186 | _down_left := _cent_idex + _block_size - 1 187 | if _down_left < _max_idx && (_down_left/_block_size-1) == _cent_row { 188 | _ret_round[uint32(_down_left)] = struct{}{} 189 | } 190 | } 191 | 192 | { 193 | _down := _cent_idex + _block_size 194 | if _down < _max_idx && (_down/_block_size-1) == _cent_row { 195 | _ret_round[uint32(_down)] = struct{}{} 196 | } 197 | } 198 | { 199 | _down_right := _cent_idex + _block_size + 1 200 | if _down_right < _max_idx && (_down_right/_block_size-1) == _cent_row { 201 | _ret_round[uint32(_down_right)] = struct{}{} 202 | } 203 | } 204 | 205 | return _ret_round 206 | } 207 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Logic/Aoi/AoiManager_test.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "YMsg" 5 | "testing" 6 | ) 7 | 8 | func roundTestHelp(cent_index_, block_ uint32, round_target_ []uint32) map[uint32]struct{} { 9 | _mgr := NewAoiManager(1280, 720, float64(block_)) 10 | _sure_arr := make(map[uint32]struct{}) 11 | for _, _it := range round_target_ { 12 | _sure_arr[_it] = struct{}{} 13 | } 14 | _round_arr := _mgr.getRoundBlock(cent_index_) 15 | for _it := range _round_arr { 16 | _, exists := _sure_arr[_it] 17 | if !exists { 18 | _sure_arr[_it] = struct{}{} 19 | } 20 | delete(_sure_arr, _it) 21 | } 22 | return _sure_arr 23 | } 24 | 25 | func TestAoiGetRoundIndex(t *testing.T) { 26 | 27 | _err_list := roundTestHelp(16, 5, []uint32{10, 11, 12, 15, 17, 20, 21, 22}) 28 | if len(_err_list) > 0 { 29 | t.Fatalf("[%v]", _err_list) 30 | } 31 | 32 | _err_list = roundTestHelp(0, 5, []uint32{1, 5, 6}) 33 | if len(_err_list) > 0 { 34 | t.Fatalf("[%v]", _err_list) 35 | } 36 | 37 | _err_list = roundTestHelp(4, 5, []uint32{3, 8, 9}) 38 | if len(_err_list) > 0 { 39 | t.Fatalf("[%v]", _err_list) 40 | } 41 | 42 | _err_list = roundTestHelp(2, 5, []uint32{1, 6, 7, 8, 3}) 43 | if len(_err_list) > 0 { 44 | t.Fatalf("[%v]", _err_list) 45 | } 46 | 47 | _err_list = roundTestHelp(20, 5, []uint32{15, 16, 21}) 48 | if len(_err_list) > 0 { 49 | t.Fatalf("[%v]", _err_list) 50 | } 51 | _err_list = roundTestHelp(22, 5, []uint32{16, 17, 18, 21, 23}) 52 | if len(_err_list) > 0 { 53 | t.Fatalf("[%v]", _err_list) 54 | } 55 | _err_list = roundTestHelp(24, 5, []uint32{18, 19, 23}) 56 | if len(_err_list) > 0 { 57 | t.Fatalf("[%v]", _err_list) 58 | } 59 | } 60 | 61 | func TestAoiGetRoundIndex10(t *testing.T) { 62 | 63 | _err_list := roundTestHelp(0, 10, []uint32{1, 10, 11}) 64 | if len(_err_list) > 0 { 65 | t.Fatalf("[%v]", _err_list) 66 | } 67 | 68 | _err_list = roundTestHelp(11, 10, []uint32{0, 1, 2, 10, 11, 12, 20, 21, 22}) 69 | if len(_err_list) > 0 { 70 | t.Fatalf("[%v]", _err_list) 71 | } 72 | } 73 | 74 | func CalcIndexHelp(x_, y_ float64, tar_idx_ uint32) bool { 75 | _mgr := NewAoiManager(1280, 720, float64(10)) 76 | _cal_idx := _mgr.CalcIndex(YMsg.PositionXY{x_, y_}) 77 | return _cal_idx == tar_idx_ 78 | } 79 | func TestCalcIndex(t *testing.T) { 80 | if !CalcIndexHelp(0,0,0){ 81 | t.Fatal() 82 | } 83 | if !CalcIndexHelp(128,0,1){ 84 | t.Fatal() 85 | } 86 | if !CalcIndexHelp(128,73,11){ 87 | t.Fatal() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Logic/Aoi/GoAoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | ) 6 | 7 | type GoAoiCellAction struct { 8 | m_action uint32 9 | m_action_obj GoAoiObj 10 | } 11 | 12 | const ( 13 | GO_AOI_CELL_ACTION_ENTER = iota 14 | GO_AOI_CELL_ACTION_NOTIFY_ENTER 15 | GO_AOI_CELL_ACTION_UPDATE 16 | GO_AOI_CELL_ACTION_NOTIFY_QUIT 17 | GO_AOI_CELL_ACTION_QUIT 18 | ) 19 | 20 | type GoAoiCell struct { 21 | m_obj_list map[uint64]GoAoiObj 22 | m_mgr_chan *YTool.SyncQueue 23 | M_obj_action chan GoAoiCellAction 24 | m_close chan struct{} 25 | } 26 | 27 | func NewGoAoiCell(mgr_chan_ *YTool.SyncQueue) *GoAoiCell { 28 | _cell := &GoAoiCell{ 29 | m_obj_list: make(map[uint64]GoAoiObj), 30 | m_mgr_chan: mgr_chan_, 31 | M_obj_action: make(chan GoAoiCellAction, 1000), 32 | m_close: make(chan struct{}), 33 | } 34 | go func() { 35 | for { 36 | select { 37 | case <-_cell.M_obj_action: 38 | for _action := range _cell.M_obj_action { 39 | if len(_cell.M_obj_action) == 0 { 40 | break 41 | } 42 | switch _action.m_action { 43 | case GO_AOI_CELL_ACTION_ENTER: 44 | _cell.enterCell(_action.m_action_obj) 45 | case GO_AOI_CELL_ACTION_NOTIFY_ENTER: 46 | _cell.notifyEnterCell(_action.m_action_obj) 47 | case GO_AOI_CELL_ACTION_UPDATE: 48 | _cell.updateCell(_action.m_action_obj) 49 | case GO_AOI_CELL_ACTION_NOTIFY_QUIT: 50 | _cell.notifyQuitCell(_action.m_action_obj) 51 | case GO_AOI_CELL_ACTION_QUIT: 52 | _cell.quitCell(_action.m_action_obj) 53 | } 54 | } 55 | 56 | case <-_cell.m_close: 57 | return 58 | } 59 | } 60 | }() 61 | return _cell 62 | } 63 | 64 | func (cell *GoAoiCell) EnterCell(enter_ GoAoiObj) { 65 | cell.M_obj_action <- GoAoiCellAction{ 66 | GO_AOI_CELL_ACTION_ENTER, 67 | enter_, 68 | } 69 | } 70 | func (cell *GoAoiCell) NotifyEnterCell(enter_ GoAoiObj) { 71 | cell.M_obj_action <- GoAoiCellAction{ 72 | GO_AOI_CELL_ACTION_NOTIFY_ENTER, 73 | enter_, 74 | } 75 | } 76 | func (cell *GoAoiCell) QuitCell(quit_ GoAoiObj) { 77 | cell.M_obj_action <- GoAoiCellAction{ 78 | GO_AOI_CELL_ACTION_QUIT, 79 | quit_, 80 | } 81 | } 82 | func (cell *GoAoiCell) NotifyQuitCell(enter_ GoAoiObj) { 83 | cell.M_obj_action <- GoAoiCellAction{ 84 | GO_AOI_CELL_ACTION_NOTIFY_QUIT, 85 | enter_, 86 | } 87 | } 88 | func (cell *GoAoiCell) UpdateCell(enter_ GoAoiObj) { 89 | cell.M_obj_action <- GoAoiCellAction{ 90 | GO_AOI_CELL_ACTION_UPDATE, 91 | enter_, 92 | } 93 | } 94 | 95 | func (cell *GoAoiCell) enterCell(enter_ GoAoiObj) { 96 | cell.m_obj_list[enter_.M_uid] = enter_ 97 | /* _, exists := cell.m_watch_list[enter_.M_module_uid] 98 | if !exists { 99 | cell.m_watch_list[enter_.M_module_uid] = make(map[uint64]struct{}) 100 | }*/ 101 | } 102 | 103 | func (cell *GoAoiCell) notifyEnterCell(enter_ GoAoiObj) { 104 | _func := func(notify_, action_ GoAoiObj) { 105 | if notify_.PositionXY.Distance(action_.PositionXY) < notify_.M_view_range { 106 | //cell.m_watch_list[notify_.M_module_uid][action_.M_module_uid] = struct{}{} 107 | cell.m_mgr_chan.Add(GoAoiAction{ 108 | GO_AOI_ACTION_ENTER, 109 | notify_.M_uid, 110 | action_.M_uid, 111 | }) 112 | } 113 | } 114 | for _, _it := range cell.m_obj_list { 115 | _func(_it, enter_) 116 | _func(enter_, _it) 117 | } 118 | } 119 | 120 | func (cell *GoAoiCell) quitCell(quit_ GoAoiObj) { 121 | 122 | delete(cell.m_obj_list, quit_.M_uid) 123 | } 124 | 125 | func (cell *GoAoiCell) notifyQuitCell(quit_ GoAoiObj) { 126 | for _, _it := range cell.m_obj_list { 127 | cell.m_mgr_chan.Add(GoAoiAction{ 128 | GO_AOI_ACTION_QUIT, 129 | quit_.M_uid, 130 | _it.M_uid, 131 | }) 132 | cell.m_mgr_chan.Add(GoAoiAction{ 133 | GO_AOI_ACTION_QUIT, 134 | _it.M_uid, 135 | quit_.M_uid, 136 | }) 137 | } 138 | } 139 | 140 | func (cell *GoAoiCell) updateCell(enter_ GoAoiObj) { 141 | _func := func(notify_, action_ GoAoiObj) { 142 | _, exists := cell.m_obj_list[enter_.M_uid] 143 | if exists { 144 | cell.m_obj_list[enter_.M_uid] = enter_ 145 | } 146 | if action_.PositionXY.Distance(notify_.PositionXY) < action_.M_view_range { 147 | 148 | cell.m_mgr_chan.Add(GoAoiAction{ 149 | GO_AOI_ACTION_UPDATE, 150 | action_.M_uid, 151 | notify_.M_uid, 152 | }) 153 | 154 | } else { 155 | 156 | cell.m_mgr_chan.Add(GoAoiAction{ 157 | GO_AOI_ACTION_QUIT, 158 | action_.M_uid, 159 | notify_.M_uid, 160 | }) 161 | 162 | } 163 | } 164 | for _, _it := range cell.m_obj_list { 165 | _func(enter_, _it) 166 | _func(_it, enter_) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Logic/Aoi/GoAoiManager.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 6 | ) 7 | 8 | type GoAoiMoveCallBack func(notify_, action_ uint64) 9 | type GoAoiEnterCallBack func(notify_, action_ uint64) 10 | type GoAoiQuitCallBack func(notify_, action_ uint64) 11 | 12 | const ( 13 | GO_AOI_ACTION_ENTER = iota 14 | GO_AOI_ACTION_UPDATE 15 | GO_AOI_ACTION_QUIT 16 | ) 17 | 18 | type GoAoiObj struct { 19 | M_uid uint64 20 | Msg.PositionXY 21 | M_view_range float64 22 | } 23 | 24 | type GoAoiAction struct { 25 | m_action uint32 26 | m_notify_obj uint64 27 | m_action_obj uint64 28 | } 29 | 30 | type GoAoiManager struct { 31 | M_height float64 32 | M_width float64 33 | m_aoi_list map[uint32]*GoAoiCell 34 | M_current_index map[uint64]uint32 35 | m_block_height float64 36 | m_block_width float64 37 | m_block_size float64 38 | 39 | m_enter_callback GoAoiEnterCallBack 40 | m_update_callback GoAoiMoveCallBack 41 | m_quit_callback GoAoiQuitCallBack 42 | //m_action_list chan GoAoiAction 43 | m_action_list *YTool.SyncQueue 44 | } 45 | 46 | func NewGoAoiManager(width_, height_, block_size_ float64) *GoAoiManager { 47 | _mgr := &GoAoiManager{ 48 | m_aoi_list: make(map[uint32]*GoAoiCell), 49 | M_current_index: make(map[uint64]uint32), 50 | m_action_list: YTool.NewSyncQueue(), 51 | } 52 | _mgr.M_height = height_ 53 | _mgr.M_width = width_ 54 | _mgr.m_block_height = height_ / block_size_ 55 | _mgr.m_block_width = width_ / block_size_ 56 | _mgr.m_block_size = block_size_ 57 | return _mgr 58 | } 59 | 60 | func (mgr *GoAoiManager) Init(move_call_ GoAoiMoveCallBack, enter_call_ GoAoiEnterCallBack, quit_call_ GoAoiQuitCallBack) { 61 | 62 | mgr.m_enter_callback = enter_call_ 63 | mgr.m_update_callback = move_call_ 64 | mgr.m_quit_callback = quit_call_ 65 | 66 | for _row_idx := uint32(0); _row_idx < uint32(mgr.m_block_size); _row_idx++ { 67 | for _col_idx := uint32(0); _col_idx < uint32(mgr.m_block_size); _col_idx++ { 68 | _cell := NewGoAoiCell(mgr.m_action_list) 69 | mgr.m_aoi_list[mgr.buildIndex(_row_idx, _col_idx)] = _cell 70 | } 71 | } 72 | } 73 | 74 | func (mgr *GoAoiManager) Update() { 75 | for { 76 | if mgr.m_action_list.Len() == 0 { 77 | break 78 | } 79 | _act := mgr.m_action_list.Pop().(GoAoiAction) 80 | switch _act.m_action { 81 | case GO_AOI_ACTION_ENTER: 82 | mgr.m_enter_callback(_act.m_notify_obj, _act.m_action_obj) 83 | case GO_AOI_ACTION_UPDATE: 84 | mgr.m_update_callback(_act.m_notify_obj, _act.m_action_obj) 85 | case GO_AOI_ACTION_QUIT: 86 | mgr.m_quit_callback(_act.m_notify_obj, _act.m_action_obj) 87 | } 88 | } 89 | 90 | } 91 | 92 | func (mgr *GoAoiManager) Enter(enter_ GoAoiObj, pos_ Msg.PositionXY) { 93 | _current_index := mgr.CalcIndex(pos_) 94 | _cell := mgr.m_aoi_list[_current_index] 95 | _cell.EnterCell(enter_) 96 | _round_arr := mgr.getRoundBlock(_current_index) 97 | for _it := range _round_arr { 98 | _cell, exists := mgr.m_aoi_list[_it] 99 | if exists { 100 | _cell.NotifyEnterCell(enter_) 101 | } 102 | } 103 | mgr.M_current_index[enter_.M_uid] = _current_index 104 | } 105 | 106 | func (mgr *GoAoiManager) Quit(quit_ GoAoiObj, pos_ Msg.PositionXY) { 107 | _current_index := mgr.CalcIndex(pos_) 108 | _cell := mgr.m_aoi_list[_current_index] 109 | _cell.QuitCell(quit_) 110 | _round_arr := mgr.getRoundBlock(_current_index) 111 | for _it := range _round_arr { 112 | _cell, exists := mgr.m_aoi_list[_it] 113 | if exists { 114 | _cell.NotifyQuitCell(quit_) 115 | } 116 | } 117 | delete(mgr.M_current_index, quit_.M_uid) 118 | } 119 | 120 | func (mgr *GoAoiManager) Move(move_ GoAoiObj, pos_ Msg.PositionXY) { 121 | 122 | _old_round_arr := mgr.getOldRoundBlock(move_.M_uid) 123 | 124 | _current_index := mgr.CalcIndex(pos_) 125 | _new_round_arr := mgr.getRoundBlock(_current_index) 126 | 127 | if _current_index != mgr.M_current_index[move_.M_uid] { 128 | _enter_cell := mgr.m_aoi_list[_current_index] 129 | _enter_cell.EnterCell(move_) 130 | 131 | } 132 | _enter_cell := getDiff(_new_round_arr, _old_round_arr) 133 | for _it := range _enter_cell { 134 | _cell, exists := mgr.m_aoi_list[_it] 135 | if exists { 136 | _cell.NotifyEnterCell(move_) 137 | } 138 | } 139 | 140 | _update_cell := getDiff(_new_round_arr, _enter_cell) 141 | for _it := range _update_cell { 142 | _cell, exists := mgr.m_aoi_list[_it] 143 | if exists { 144 | _cell.UpdateCell(move_) 145 | } 146 | } 147 | if _current_index != mgr.M_current_index[move_.M_uid] { 148 | _quit_cell := mgr.m_aoi_list[mgr.M_current_index[move_.M_uid]] 149 | _quit_cell.QuitCell(move_) 150 | } 151 | _quit_cell := getDiff(_old_round_arr, _new_round_arr) 152 | for _it := range _quit_cell { 153 | _cell, exists := mgr.m_aoi_list[_it] 154 | if exists { 155 | _cell.NotifyQuitCell(move_) 156 | } 157 | } 158 | mgr.M_current_index[move_.M_uid] = _current_index 159 | 160 | } 161 | 162 | func (mgr *GoAoiManager) CalcIndex(xy_ Msg.PositionXY) uint32 { 163 | return mgr.buildIndex(uint32(xy_.M_x/mgr.m_block_width), uint32(xy_.M_y/mgr.m_block_height)) 164 | } 165 | 166 | func (mgr *GoAoiManager) buildIndex(row_, col_ uint32) uint32 { 167 | return row_ + col_*uint32(mgr.m_block_size) 168 | } 169 | 170 | func (mgr *GoAoiManager) getOldRoundBlock(uid_ uint64) map[uint32]struct{} { 171 | _old_index := mgr.M_current_index[uid_] 172 | return mgr.getRoundBlock(_old_index) 173 | } 174 | 175 | func (mgr *GoAoiManager) getRoundBlock(cent_index_ uint32) map[uint32]struct{} { 176 | _ret_round := make(map[uint32]struct{}) 177 | _cent_idex := int(cent_index_) 178 | _block_size := int(mgr.m_block_size) 179 | 180 | _max_idx := int(mgr.m_block_size * mgr.m_block_size) 181 | 182 | _cent_row := int(cent_index_ / uint32(mgr.m_block_size)) 183 | _ret_round[cent_index_] = struct{}{} 184 | { 185 | _left_up := _cent_idex - _block_size - 1 186 | if _left_up >= 0 && (_left_up/_block_size+1) == _cent_row { 187 | _ret_round[uint32(_left_up)] = struct{}{} 188 | } 189 | } 190 | 191 | { 192 | _up := _cent_idex - _block_size 193 | if _up >= 0 && (_up/_block_size+1) == _cent_row { 194 | _ret_round[uint32(_up)] = struct{}{} 195 | } 196 | } 197 | { 198 | _up_right := _cent_idex - _block_size + 1 199 | if _up_right >= 0 && (_up_right/_block_size+1) == _cent_row { 200 | _ret_round[uint32(_up_right)] = struct{}{} 201 | } 202 | } 203 | 204 | { 205 | _left := _cent_idex - 1 206 | if _left >= 0 && (_left/_block_size) == _cent_row { 207 | _ret_round[uint32(_left)] = struct{}{} 208 | } 209 | } 210 | { 211 | _right := _cent_idex + 1 212 | if _right >= 0 && (_right/_block_size) == _cent_row { 213 | _ret_round[uint32(_right)] = struct{}{} 214 | } 215 | } 216 | 217 | { 218 | _down_left := _cent_idex + _block_size - 1 219 | if _down_left < _max_idx && (_down_left/_block_size-1) == _cent_row { 220 | _ret_round[uint32(_down_left)] = struct{}{} 221 | } 222 | } 223 | 224 | { 225 | _down := _cent_idex + _block_size 226 | if _down < _max_idx && (_down/_block_size-1) == _cent_row { 227 | _ret_round[uint32(_down)] = struct{}{} 228 | } 229 | } 230 | { 231 | _down_right := _cent_idex + _block_size + 1 232 | if _down_right < _max_idx && (_down_right/_block_size-1) == _cent_row { 233 | _ret_round[uint32(_down_right)] = struct{}{} 234 | } 235 | } 236 | 237 | return _ret_round 238 | } 239 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Logic/Aoi/GoNineGridAoi.go: -------------------------------------------------------------------------------- 1 | package aoi 2 | 3 | type GoNineGirdAoiCell struct { 4 | m_watch_list map[uint64]struct{} 5 | } 6 | 7 | func NewGoNineGirdAoiCell() *GoNineGirdAoiCell { 8 | _cell := &GoNineGirdAoiCell{ 9 | m_watch_list: make(map[uint64]struct{}), 10 | } 11 | return _cell 12 | } 13 | func (cell *GoNineGirdAoiCell)GetWatch()map[uint64]struct{}{ 14 | return cell.m_watch_list 15 | } 16 | func (cell *GoNineGirdAoiCell)Watch(uid_ uint64){ 17 | cell.m_watch_list[uid_] = struct{}{} 18 | } 19 | 20 | func (cell *GoNineGirdAoiCell)Forget(uid_ uint64){ 21 | delete(cell.m_watch_list, uid_) 22 | } 23 | 24 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Logic/Move/MoveControl.go: -------------------------------------------------------------------------------- 1 | package move 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 6 | "time" 7 | ) 8 | 9 | type MoveControl struct { 10 | M_pos Msg.PositionXY 11 | M_tar Msg.PositionXY 12 | M_next_path Msg.PositionXY 13 | m_path_queue *YTool.Queue 14 | m_path_cache []Msg.PositionXY 15 | M_speed float64 16 | m_last_move_time time.Time 17 | M_view_range float64 18 | } 19 | 20 | func (c *MoveControl) CanToNextPath() bool { 21 | if c.m_path_queue == nil { 22 | return false 23 | } 24 | if c.m_path_queue.Length() == 0 { 25 | return false 26 | } 27 | 28 | return true 29 | } 30 | 31 | func (c *MoveControl) DebugString() string { 32 | _str := "" 33 | if c.m_path_queue == nil { 34 | return _str 35 | } 36 | for _idx := 0; _idx < c.m_path_queue.Length(); _idx++ { 37 | _str += c.m_path_queue.Get(_idx).(Msg.PositionXY).DebugString() 38 | } 39 | 40 | return _str 41 | } 42 | 43 | func (c *MoveControl) GetPathNode() []Msg.PositionXY { 44 | return c.m_path_cache 45 | } 46 | 47 | func (c *MoveControl) toNextPath() { 48 | c.M_next_path = c.m_path_queue.Pop().(Msg.PositionXY) 49 | } 50 | 51 | func (c *MoveControl) MoveQueue(path_queue_ *YTool.Queue) { 52 | c.m_path_queue = path_queue_ 53 | c.toNextPath() 54 | _path_node := make([]Msg.PositionXY, 0) 55 | for _idx := 0; _idx < c.m_path_queue.Length(); _idx++ { 56 | _path_node = append(_path_node, c.m_path_queue.Get(_idx).(Msg.PositionXY)) 57 | } 58 | c.m_path_cache = _path_node 59 | 60 | } 61 | func (c *MoveControl) MoveTarget(tar_ Msg.PositionXY) { 62 | c.M_tar = tar_ 63 | } 64 | 65 | func (c *MoveControl) MoveUpdate(time_ time.Time) bool { 66 | defer func() { 67 | c.m_last_move_time = time_ 68 | }() 69 | 70 | if c.M_pos.IsSame(c.M_tar) { 71 | return false 72 | } 73 | 74 | if c.M_pos.IsSame(c.M_next_path) { 75 | if !c.CanToNextPath() { 76 | return false 77 | } 78 | c.toNextPath() 79 | } 80 | 81 | _distance := c.M_pos.Distance(c.M_next_path) 82 | 83 | _interval_time := time_.Sub(c.m_last_move_time).Seconds() 84 | _this_move_distance := _interval_time * c.M_speed 85 | 86 | if _distance < _this_move_distance { 87 | c.M_pos = c.M_next_path 88 | return true 89 | } 90 | _precent := _this_move_distance / _distance 91 | 92 | _distance_pos := c.M_pos.DistancePosition(c.M_next_path) 93 | 94 | c.M_pos.M_x += _distance_pos.M_x * _precent 95 | c.M_pos.M_y += _distance_pos.M_y * _precent 96 | 97 | return true 98 | } 99 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/yxinyi/YCServer/engine/BaseModule/NetModule" 6 | "github.com/yxinyi/YCServer/engine/YModule" 7 | "github.com/yxinyi/YCServer/engine/YMsg" 8 | "github.com/yxinyi/YCServer/engine/YNode" 9 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Server/Module/Map" 10 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Server/Module/MapManager" 11 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Server/Module/UserManager" 12 | "log" 13 | "net/http" 14 | _ "net/http/pprof" 15 | ) 16 | 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | YNode.ModuleCreateFuncRegister("NewMap", Map.NewInfo) 22 | YNode.ModuleCreateFuncRegister("NetModule", NetModule.NewInfo) 23 | YNode.ModuleCreateFuncRegister("MapManager", MapManager.NewInfo) 24 | YNode.ModuleCreateFuncRegister("UserManager", UserManager.NewInfo) 25 | YNode.SetNodeID(0) 26 | YNode.Register( 27 | YNode.NewModuleInfo("NetModule",0), 28 | YNode.NewModuleInfo("NewMap",1), 29 | YNode.NewModuleInfo("MapManager",0), 30 | YNode.NewModuleInfo("UserManager",0), 31 | ) 32 | go func(){ 33 | log.Fatal(http.ListenAndServe("0.0.0.0:9999", nil)) 34 | }() 35 | 36 | 37 | YNode.RPCCall(YModule.NewRPCMsg(YMsg.ToAgent("NetModule"), "Listen", "0.0.0.0:20000")) 38 | YNode.Start() 39 | } 40 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Module/Map/README.md: -------------------------------------------------------------------------------- 1 | # 无缝大地图设计 2 | 3 | ## 无缝大地图服务器与非无缝大地图设计区别 4 | 5 | 对于 普通的MMO 玩法来说, 往往有一个 map 对象来承载地图上数据,在每帧内对这些数据进行处理来完成地图上需要发生的逻辑,玩家其实也就是挂在对象上的一个数据,在地图切换时,往往玩家行走至地图上一个传送点上开始切图,如果发生了`地图A`切换到`地图B`的情况,从内存角度来说,也只不过是将 mapA 上的玩家对象剪切至 mapB 对象上。 6 | 7 | 那么对于无缝地图来说,在地图切换这部分,其实也没有太大区别,可以理解成传送点变成了传送区域,地图A的边界的所有边缘都可以进行地图切换,从mapA到mapB的过程中也是通过剪切内存来处理玩家的数据。 8 | 9 | 但是与非无缝大地图不同的是,当玩家处于 mapA与mapB的边缘时,虽然玩家数据在mapA上,但是mapB上的玩家也必须能看到处于mapA的玩家,且 mapA 地图上的玩家与 mapB地图上的玩家也可以像在一张地图上一样进行交互,战斗。 10 | 11 | 而这个跨进程交互就是无缝大地图的难点了。 12 | 13 | ## 从单进程说起 14 | 15 | 在mmo的地图逻辑中,玩家需要与当前场景的对象进行交互,比如 NPC ,怪物,场景物品,且这些对象往往是对全地图的玩家开放,也就意味着,一个对象的状态发生改变时,也需要对全地图的玩家进行广播,才能让发起主动交互玩家外的玩家获取被交互对象的最新的正确状态,基于此来实现各种效果,如 怪物死亡,NPC移动,场景物品的掉落拾取,技能释放等逻辑。 16 | 17 | 那么在上述需求的前提下,我们要怎么进行同步呢? 18 | 19 | 最简单方法是,当一个对象发生变化时,可以遍历当前地图的所有玩家对他们进行消息同步来实现我们的目的,但是显而易见我们可以想象到这个方案的弊处,如果一个地图大小为1000X1000,玩家屏幕大小为10X10 20 | 那么当玩家处于地图左上角时,右下角其他玩家的对象操作,其实我们是不关心的,但是却依然进行了同步我们在进行游戏行为时更关心的是我们当前屏幕能看到的对象,且一个地图如果有100个玩家,每个玩家1秒钟都要进行一次行为,那么一秒钟的消息数量即为 100 * 100 达到1万条,这也显然是有很大的优化空间的。所以有了AOI的说法。 21 | 22 | AOI 即 arena of interests ,感兴趣的区域,其实现方案不在本文的讨论范围内,主要有 灯塔,九宫格,十字链表 法三种,各有优劣,但说到底,这些都是手段,我们还是要探究为什么能解决上一段提出的优化,简单的了解思路后即使不进行实现,我们也可以知晓其原理,其会将地图进行分割,并只找出自己感兴趣的区域,在一个对象发生变化后,也只同步给周围的对象,虽然从复杂度来说也算是 n^2,但是由于同步的玩家变少了,从原来的 100*100 缩减成 5\*5 * N甚至更低,有一个数量级的优化,也就能提高单地图的逻辑承载上限。 23 | 24 | 不过即使有各种优化,包括多线程级优化,却永远无法突破物理机的限制,即使单进程实现了1000张地图逻辑,那他也会在第1001张地图的时候表现出瓶颈,因为无论是多么高的配置,单机的承载能力是有上限的。在此限制前提下,也就是意味着使用单进程来实现无边界的无缝地图的服务器逻辑是不可行的。 25 | 26 | 那如何完成我们的无缝大地图的服务器框架,我曾在一段时间内反复思考这个问题,有想出一些方案似乎能解决,但没有时间去实现,且也想更深入的思考,直到最近看了kbengine 的设计思路后,才发现自己的方案已经有人实践并证明了其可行性。 27 | 28 | 核心点就2个,一个是分布式AOI,另一个是主从数据 29 | 30 | 由于单进程的物理限制,一个可以无限扩展的无缝大地图一定会发生两张相邻的地图可能被部署在两台物理机上的情况,也就以为着当玩家在两张地图边缘时,要怎么处理其行为,以及如何让边缘的玩家知道他的动作。 31 | 32 | 33 | 34 | ## 需要解决的问题 35 | 36 | 先试想一下玩家在地图边缘时需要做些什么事情 37 | 38 | 一个是玩家的视野,在两张地图边缘处也是要有玩家视野控制的逻辑,也就是我们的AOI逻辑需要独立在地图逻辑之外,也就是一个AOI可以控制多张地图逻辑。 39 | 40 | 一个是玩家间的交互,在边缘时玩家间的战斗处理,如果两个进程的玩家发生交互该如何处理,该哪个进程处理,该如何通知. 41 | 42 | 43 | 44 | ## 分布式AOI 45 | 46 | ## GHOST 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Module/MapManager/MapManager.go: -------------------------------------------------------------------------------- 1 | package MapManager 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YModule" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 8 | _ "github.com/yxinyi/YCServer/examples/AoiAstarExample/Server/Module/Map" 9 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Server/Module/UserManager" 10 | "math" 11 | ) 12 | 13 | type Info struct { 14 | YModule.BaseInter 15 | M_map_pool map[uint64]Msg.MapLoad 16 | } 17 | 18 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 19 | _info := &Info{ 20 | M_map_pool: make(map[uint64]Msg.MapLoad), 21 | } 22 | _info.Info = YModule.NewInfo(node_) 23 | return _info 24 | } 25 | func (i *Info) Init() { 26 | i.Info.Init(i) 27 | } 28 | 29 | func (i *Info) Close() { 30 | } 31 | 32 | func (i *Info) RPC_MapRegister(load_ Msg.MapLoad) { 33 | i.M_map_pool[load_.M_map_uid] = load_ 34 | } 35 | 36 | func (i *Info) RPC_MapLoadChange(load_ Msg.MapLoad) { 37 | i.M_map_pool[load_.M_map_uid] = load_ 38 | } 39 | 40 | func (i *Info) GetLeastLoadMap() uint64 { 41 | _max_load := uint32(math.MaxUint32) 42 | _tar_map := uint64(0) 43 | for _, _it := range i.M_map_pool { 44 | if _it.M_load < _max_load { 45 | _max_load = _it.M_load 46 | } 47 | _tar_map = _it.M_map_uid 48 | } 49 | return _tar_map 50 | } 51 | 52 | const ( 53 | FirstMapUID = 1 54 | ) 55 | 56 | 57 | func (i *Info) RPC_FirstEnterMap(user_ UserManager.User) { 58 | i.Info.RPCCall(YMsg.ToAgent("Map", FirstMapUID), "UserEnterMap", user_) 59 | i.Info.RPCCall(YMsg.ToAgent("UserManager"), "UserChangeCurrentMap", user_.M_uid, FirstMapUID) 60 | } 61 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Module/README.md: -------------------------------------------------------------------------------- 1 | ### 登录及进入地图 2 | 3 | ```sequence 4 | Cli->UserMgr:MSG_C2S_Login 5 | Cli->UserMgr:MSG_C2S_EnterMap 6 | UserMgr->MapMgr:RPC_GetLeastLoadMap 7 | MapMgr->UserMgr:RPC_GetLeastLoadMap 8 | UserMgr->Map:RPC_UserEnterMap 9 | Map->UserMgr:RPC_UserEnterMap 10 | Map->UserMgr:RPC_MapLoadChange 11 | UserMgr->Cli:MSG_S2C_Login 12 | ``` 13 | 14 | ### 移动 15 | 16 | ```sequence 17 | Cli->UserMgr:MSG_C2S_UserMove 18 | UserMgr->Map:RPC_UserMove 19 | Map->UserMgr:RPC_UserMove 20 | UserMgr->Cli:MSG_S2C_UserMove 21 | ``` 22 | 23 | ### 玩家移动并且通知视野内玩家 24 | 25 | ```sequence 26 | Cli->UserMgr:MSG_C2S_UserMove 27 | UserMgr->Map:RPC_UserMove 28 | Map->OtherClis_1:MSG_S2C_EntityChange 29 | Map->OtherClis_2:MSG_S2C_EntityChange 30 | Map->OtherClis_3:MSG_S2C_EntityChange 31 | Map->UserMgr:RPC_UserMove 32 | UserMgr->Cli:MSG_S2C_UserMove 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Module/UserManager/UserInfo.go: -------------------------------------------------------------------------------- 1 | package UserManager 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YEntity" 5 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 6 | move "github.com/yxinyi/YCServer/examples/AoiAstarExample/Server/Logic/Move" 7 | "time" 8 | ) 9 | 10 | type User struct { 11 | YEntity.Info 12 | M_uid uint64 13 | M_current_map uint64 14 | M_session_id uint64 15 | M_is_rotbot bool 16 | move.MoveControl 17 | } 18 | 19 | func NewUser(uid_ uint64, session_id_ uint64) *User { 20 | return &User{ 21 | M_uid: uid_, 22 | M_session_id: session_id_, 23 | } 24 | } 25 | 26 | 27 | func (u *User) ToClientJson() Msg.UserData { 28 | _user_msg := Msg.UserData{ 29 | M_pos: u.M_pos, 30 | M_uid: u.M_uid, 31 | } 32 | return _user_msg 33 | } 34 | 35 | func (u *User) MoveUpdate(time_ time.Time) bool { 36 | return u.MoveControl.MoveUpdate(time_) 37 | } 38 | 39 | func (u *User) CanToNextPath() bool { 40 | return u.MoveControl.CanToNextPath() 41 | } 42 | -------------------------------------------------------------------------------- /examples/AoiAstarExample/Server/Module/UserManager/UserManager.go: -------------------------------------------------------------------------------- 1 | package UserManager 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YModule" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "github.com/yxinyi/YCServer/examples/AoiAstarExample/Msg" 8 | ) 9 | 10 | type Info struct { 11 | YModule.BaseInter 12 | M_user_pool map[uint64]*User 13 | } 14 | 15 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 16 | _info := &Info{ 17 | M_user_pool: make(map[uint64]*User), 18 | } 19 | _info.Info = YModule.NewInfo(node_) 20 | return _info 21 | } 22 | func (i *Info) Init() { 23 | i.Info.Init(i) 24 | } 25 | 26 | func (i *Info) Close() { 27 | } 28 | 29 | func (i *Info) MSG_C2S_Login(s_ uint64, msg_ Msg.C2S_Login) { 30 | _, exists := i.M_user_pool[s_] 31 | if !exists { 32 | i.M_user_pool[s_] = NewUser(s_, s_) 33 | } 34 | i.Info.SendNetMsgJson(s_, Msg.S2C_Login{ 35 | i.M_user_pool[s_].ToClientJson(), 36 | }) 37 | } 38 | 39 | func (i *Info) RPC_UserChangeCurrentMap(s_, map_uid_ uint64) { 40 | _user := i.M_user_pool[s_] 41 | if _user != nil { 42 | _user.M_current_map = map_uid_ 43 | } 44 | } 45 | 46 | func (i *Info) MSG_C2S_FirstEnterMap(s_ uint64, msg_ Msg.C2S_FirstEnterMap) { 47 | 48 | _user := i.M_user_pool[s_] 49 | if _user != nil { 50 | i.Info.RPCCall(YMsg.ToAgent("MapManager"), "FirstEnterMap", *_user) 51 | if len(i.M_user_pool) == 1 { 52 | for idx := uint64(10); idx < 101; idx++ { 53 | _robot_user := NewUser(idx, idx) 54 | _robot_user.M_is_rotbot = true 55 | i.M_user_pool[idx] = _robot_user 56 | 57 | i.Info.RPCCall(YMsg.ToAgent("MapManager"), "FirstEnterMap", *_robot_user) 58 | } 59 | } 60 | } 61 | } 62 | 63 | func (i *Info) MSG_C2S_UserMove(s_ uint64, msg_ Msg.C2S_UserMove) { 64 | _user := i.M_user_pool[s_] 65 | if _user == nil { 66 | return 67 | } 68 | 69 | i.Info.RPCCall(YMsg.ToAgent("Map", _user.M_current_map), "UserMove", _user.M_uid, msg_.M_pos) 70 | } 71 | -------------------------------------------------------------------------------- /examples/NetExample/Client/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/yxinyi/YCServer/engine/YNet" 6 | "github.com/yxinyi/YCServer/examples/NetExample/Msg" 7 | _ "net/http/pprof" 8 | ) 9 | 10 | func main() { 11 | flag.Parse() 12 | _client := YNet.NewConnect() 13 | _client.Connect("127.0.0.1:20000") 14 | _client.Start() 15 | _cnt := 1 16 | for { 17 | _client.SendJson(Msg.C2S_TestMsg{ 18 | _cnt, 19 | "测试字符串", 20 | }) 21 | _client.SendJson(Msg.C2S_TestMsg_2{ 22 | _cnt, 23 | }) 24 | _cnt++ 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/NetExample/Msg/Msg.go: -------------------------------------------------------------------------------- 1 | package Msg 2 | 3 | type C2S_TestMsg struct { 4 | M_val_int int 5 | M_val_str string 6 | } 7 | type C2S_TestMsg_2 struct { 8 | M_val_int int 9 | } -------------------------------------------------------------------------------- /examples/NetExample/Server/Logic/TestModule/TestModule.go: -------------------------------------------------------------------------------- 1 | package TestModule 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "github.com/yxinyi/YCServer/examples/NetExample/Msg" 8 | ) 9 | 10 | type TestInfo struct { 11 | YModule.BaseInter 12 | } 13 | 14 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 15 | _info := &TestInfo{} 16 | _info.Info = YModule.NewInfo(node_) 17 | 18 | return _info 19 | } 20 | 21 | func (m *TestInfo) GetInfo() *YModule.Info { 22 | return m.Info 23 | } 24 | func (m *TestInfo) Init() { 25 | m.Info.Init(m) 26 | } 27 | 28 | func (m *TestInfo) Close() { 29 | 30 | } 31 | 32 | func (m *TestInfo) MSG_C2S_TestMsg(s_ uint64, msg_ Msg.C2S_TestMsg) { 33 | ylog.Info("TestModule[%v]", msg_) 34 | } 35 | 36 | func (m *TestInfo) MSG_C2S_TestMsg_2(s_ uint64, msg_ Msg.C2S_TestMsg_2) { 37 | ylog.Info("TestModule [%v]", msg_) 38 | } 39 | -------------------------------------------------------------------------------- /examples/NetExample/Server/Logic/TestModule2/TestModule.go: -------------------------------------------------------------------------------- 1 | package TestModule2 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "github.com/yxinyi/YCServer/examples/NetExample/Msg" 8 | ) 9 | 10 | type Info struct { 11 | YModule.BaseInter 12 | } 13 | 14 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 15 | _info := &Info{} 16 | _info.Info = YModule.NewInfo(node_) 17 | return _info 18 | } 19 | 20 | func (m *Info) Init() { 21 | m.Info.Init(m) 22 | } 23 | 24 | func (m *Info) Loop() { 25 | m.Info.Loop_Msg() 26 | } 27 | func (m *Info) Close() { 28 | } 29 | 30 | func (m *Info) MSG_C2S_TestMsg(s_ uint64, msg_ Msg.C2S_TestMsg) { 31 | ylog.Info("TestModule2[%v]", msg_) 32 | } 33 | 34 | func (m *Info) MSG_C2S_TestMsg_2(s_ uint64, msg_ Msg.C2S_TestMsg_2) { 35 | ylog.Info("TestModule2 [%v]", msg_) 36 | } 37 | -------------------------------------------------------------------------------- /examples/NetExample/Server/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/BaseModule/NetModule" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YMsg" 7 | "github.com/yxinyi/YCServer/engine/YNode" 8 | "github.com/yxinyi/YCServer/examples/NetExample/Server/Logic/TestModule" 9 | "github.com/yxinyi/YCServer/examples/NetExample/Server/Logic/TestModule2" 10 | _ "net/http/pprof" 11 | ) 12 | 13 | 14 | func main() { 15 | YNode.ModuleCreateFuncRegister("NetModule", NetModule.NewInfo) 16 | YNode.ModuleCreateFuncRegister("TestModule", TestModule.NewInfo) 17 | YNode.ModuleCreateFuncRegister("TestModule2", TestModule2.NewInfo) 18 | YNode.SetNodeID(0) 19 | YNode.Register( 20 | YNode.NewModuleInfo("NetModule",0), 21 | YNode.NewModuleInfo("TestModule",0), 22 | YNode.NewModuleInfo("TestModule2",0), 23 | ) 24 | YNode.RPCCall(YModule.NewRPCMsg(YMsg.ToAgent("NetModule"), "Listen", "0.0.0.0:20000")) 25 | YNode.Start() 26 | } 27 | -------------------------------------------------------------------------------- /examples/RPCCallListExample/Logic/TestModule/TestModule.go: -------------------------------------------------------------------------------- 1 | package TestModule 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YMsg" 7 | "github.com/yxinyi/YCServer/engine/YNode" 8 | ) 9 | 10 | type TestInfo struct { 11 | YModule.BaseInter 12 | } 13 | 14 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 15 | _info := &TestInfo{} 16 | _info.Info = YModule.NewInfo(node_) 17 | 18 | return _info 19 | } 20 | 21 | func (m *TestInfo) GetInfo() *YModule.Info { 22 | return m.Info 23 | } 24 | func (m *TestInfo) Init() { 25 | m.Info.Init(m) 26 | } 27 | 28 | func (m *TestInfo) Close() { 29 | 30 | } 31 | 32 | func (m *TestInfo) RPC_Test() { 33 | m.Info.RPCCall(YMsg.ToAgent("TestModule2"), "Test_1", func(val int) { 34 | ylog.Info("Test 回调 返回值 [%v]", val) 35 | }).AfterRPC(YMsg.ToAgent("TestModule2"), "Test_2", "测试值", func(val string) { 36 | ylog.Info("Test_2 回调 返回值 [%v]", val) 37 | }).AfterRPC(YMsg.ToAgent("TestModule2"), "Test_3", 38 | ).AfterRPC(YMsg.ToAgent("TestModule2"), "Test_4", func() { 39 | ylog.Info("Test_4 回调 ") 40 | }).AfterRPC(YMsg.ToAgent("TestModule2"), "Test_5",56) 41 | 42 | m.Info.RPCCall(YMsg.ToAgent("TestModule2"), "Test_1", func(val int) { 43 | ylog.Info("Test_1 取消后续调用链 [%v]", val) 44 | m.Info.CancelCBList() 45 | }).AfterRPC(YMsg.ToAgent("TestModule2"), "Test_5", func() { 46 | ylog.Info("[错误] 没有取消 ") 47 | }) 48 | 49 | } 50 | 51 | func (m *TestInfo) RPC_Test_4(param_ int) { 52 | 53 | } 54 | -------------------------------------------------------------------------------- /examples/RPCCallListExample/Logic/TestModule/module_test.go: -------------------------------------------------------------------------------- 1 | package TestModule 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "testing" 8 | ) 9 | 10 | func init() { 11 | YNode.Register(NewInfo(YNode.Obj())) 12 | YNode.Start() 13 | } 14 | 15 | func TestModule(t *testing.T) { 16 | { 17 | msg := &YMsg.S2S_rpc_msg{} 18 | msg.M_func_name = "Test_3" 19 | msg.M_func_parameter = make([][]byte, 0) 20 | { 21 | 22 | _bytes, _ := jsoniter.Marshal(1) 23 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 24 | } 25 | { 26 | _bytes, _ := jsoniter.Marshal("123") 27 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 28 | } 29 | YNode.RPCCall(msg) 30 | } 31 | { 32 | msg := &YMsg.S2S_rpc_msg{} 33 | msg.M_func_name = "Test_4" 34 | msg.M_func_parameter = make([][]byte, 0) 35 | { 36 | 37 | _bytes, _ := jsoniter.Marshal(&YMsg.TestParam{ 38 | 123, 39 | "TESTPARAMTER", 40 | []int{ 41 | 1, 2, 3, 4, 5, 6, 7, 42 | }, 43 | }) 44 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 45 | } 46 | YNode.RPCCall(msg) 47 | } 48 | } 49 | 50 | func BenchmarkModule(b_ *testing.B) { 51 | for _idx := 0; _idx < b_.N; _idx++ { 52 | { 53 | msg := &YMsg.S2S_rpc_msg{} 54 | msg.M_func_name = "Test" 55 | msg.M_func_parameter = make([][]byte, 0) 56 | 57 | YNode.RPCCall(msg) 58 | } 59 | { 60 | msg := &YMsg.S2S_rpc_msg{} 61 | msg.M_func_name = "Test_2" 62 | msg.M_func_parameter = make([][]byte, 0) 63 | { 64 | 65 | _bytes, _ := jsoniter.Marshal(1) 66 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 67 | } 68 | YNode.RPCCall(msg) 69 | } 70 | { 71 | msg := &YMsg.S2S_rpc_msg{} 72 | msg.M_func_name = "Test_3" 73 | msg.M_func_parameter = make([][]byte, 0) 74 | { 75 | 76 | _bytes, _ := jsoniter.Marshal(1) 77 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 78 | } 79 | { 80 | _bytes, _ := jsoniter.Marshal("123") 81 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 82 | } 83 | YNode.RPCCall(msg) 84 | } 85 | { 86 | msg := &YMsg.S2S_rpc_msg{} 87 | msg.M_func_name = "Test_4" 88 | msg.M_func_parameter = make([][]byte, 0) 89 | { 90 | 91 | _bytes, _ := jsoniter.Marshal(&YMsg.TestParam{ 92 | 123, 93 | "TESTPARAMTER", 94 | []int{ 95 | 1, 2, 3, 4, 5, 6, 7, 96 | }, 97 | }) 98 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 99 | } 100 | YNode.RPCCall(msg) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/RPCCallListExample/Logic/TestModule2/TestModule.go: -------------------------------------------------------------------------------- 1 | package TestModule2 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | ) 8 | 9 | type TestInfo struct { 10 | YModule.BaseInter 11 | } 12 | 13 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 14 | _info := &TestInfo{} 15 | _info.Info = YModule.NewInfo(node_) 16 | 17 | return _info 18 | } 19 | func (m *TestInfo) GetInfo() *YModule.Info { 20 | return m.Info 21 | } 22 | func (m *TestInfo) Init() { 23 | m.Info.Init(m) 24 | } 25 | 26 | func (m *TestInfo) Close() { 27 | 28 | } 29 | 30 | func (m *TestInfo) RPC_Test_1() int { 31 | ylog.Info("[TestModule2:Test_1]") 32 | return 10 33 | } 34 | 35 | func (m *TestInfo) RPC_Test_2(val_ string) string { 36 | ylog.Info("[TestModule2:Test_2]") 37 | return val_ 38 | } 39 | 40 | func (m *TestInfo) RPC_Test_3() { 41 | ylog.Info("[TestModule2:Test_3]") 42 | } 43 | 44 | func (m *TestInfo) RPC_Test_4() { 45 | ylog.Info("[TestModule2:Test_4]") 46 | } 47 | 48 | func (m *TestInfo) RPC_Test_5(val_ uint32) { 49 | ylog.Info("[TestModule2:Test_5] [%v]",val_) 50 | } 51 | -------------------------------------------------------------------------------- /examples/RPCCallListExample/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/yxinyi/YCServer/engine/BaseModule/NetModule" 6 | "github.com/yxinyi/YCServer/engine/YModule" 7 | "github.com/yxinyi/YCServer/engine/YMsg" 8 | "github.com/yxinyi/YCServer/engine/YNode" 9 | "github.com/yxinyi/YCServer/examples/RPCCallListExample/Logic/TestModule" 10 | "github.com/yxinyi/YCServer/examples/RPCCallListExample/Logic/TestModule2" 11 | _ "net/http/pprof" 12 | ) 13 | 14 | func main() { 15 | flag.Parse() 16 | YNode.ModuleCreateFuncRegister("NetModule", NetModule.NewInfo) 17 | YNode.ModuleCreateFuncRegister("TestModule", TestModule.NewInfo) 18 | YNode.ModuleCreateFuncRegister("TestModule2", TestModule2.NewInfo) 19 | YNode.SetNodeID(0) 20 | YNode.Register( 21 | YNode.NewModuleInfo("NetModule",0), 22 | YNode.NewModuleInfo("TestModule", 0), 23 | YNode.NewModuleInfo("TestModule2", 0), 24 | ) 25 | YNode.RPCCall(YModule.NewRPCMsg(YMsg.ToAgent("TestModule"), "Test")) 26 | YNode.Start() 27 | } 28 | -------------------------------------------------------------------------------- /examples/RPCExample/Logic/TestModule/TestModule.go: -------------------------------------------------------------------------------- 1 | package TestModule 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YMsg" 7 | "github.com/yxinyi/YCServer/engine/YNode" 8 | ) 9 | 10 | type TestInfo struct { 11 | YModule.BaseInter 12 | } 13 | 14 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 15 | _info := &TestInfo{} 16 | _info.Info = YModule.NewInfo(node_) 17 | 18 | return _info 19 | } 20 | 21 | func (m *TestInfo) GetInfo() *YModule.Info { 22 | return m.Info 23 | } 24 | func (m *TestInfo) Init() { 25 | m.Info.Init(m) 26 | } 27 | 28 | func (m *TestInfo) Close() { 29 | 30 | } 31 | 32 | func (m *TestInfo) RPC_Test() { 33 | ylog.Info("TestModule RPC_Test") 34 | 35 | } 36 | 37 | func (m *TestInfo) RPC_Test_2(val_ uint32) { 38 | ylog.Info("TestModule RPC_Test_2 [%v]", val_) 39 | } 40 | 41 | func (m *TestInfo) RPC_Test_3(val_ uint32, str_ string) { 42 | ylog.Info("TestModule RPC_Test_3 [%v] [%v]", val_, str_) 43 | var _func func() 44 | _func = func() { 45 | m.Info.RPCCall(YMsg.ToAgent("TestModule2"), "Test", func() { 46 | ylog.Info("Test 回调") 47 | _func() 48 | }) 49 | } 50 | _func() 51 | } 52 | 53 | func (m *TestInfo) RPC_Test_4(param_ int) { 54 | 55 | } 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/RPCExample/Logic/TestModule/module_test.go: -------------------------------------------------------------------------------- 1 | package TestModule 2 | 3 | import ( 4 | "github.com/json-iterator/go" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "testing" 8 | ) 9 | 10 | func init() { 11 | YNode.Register(NewInfo(YNode.Obj())) 12 | YNode.Start() 13 | } 14 | 15 | func TestModule(t *testing.T) { 16 | { 17 | msg := &YMsg.S2S_rpc_msg{} 18 | msg.M_func_name = "Test_3" 19 | msg.M_func_parameter = make([][]byte, 0) 20 | { 21 | 22 | _bytes, _ := jsoniter.Marshal(1) 23 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 24 | } 25 | { 26 | _bytes, _ := jsoniter.Marshal("123") 27 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 28 | } 29 | YNode.RPCCall(msg) 30 | } 31 | { 32 | msg := &YMsg.S2S_rpc_msg{} 33 | msg.M_func_name = "Test_4" 34 | msg.M_func_parameter = make([][]byte, 0) 35 | { 36 | 37 | _bytes, _ := jsoniter.Marshal(&YMsg.TestParam{ 38 | 123, 39 | "TESTPARAMTER", 40 | []int{ 41 | 1, 2, 3, 4, 5, 6, 7, 42 | }, 43 | }) 44 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 45 | } 46 | YNode.RPCCall(msg) 47 | } 48 | } 49 | 50 | func BenchmarkModule(b_ *testing.B) { 51 | for _idx := 0; _idx < b_.N; _idx++ { 52 | { 53 | msg := &YMsg.S2S_rpc_msg{} 54 | msg.M_func_name = "Test" 55 | msg.M_func_parameter = make([][]byte, 0) 56 | 57 | YNode.RPCCall(msg) 58 | } 59 | { 60 | msg := &YMsg.S2S_rpc_msg{} 61 | msg.M_func_name = "Test_2" 62 | msg.M_func_parameter = make([][]byte, 0) 63 | { 64 | 65 | _bytes, _ := jsoniter.Marshal(1) 66 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 67 | } 68 | YNode.RPCCall(msg) 69 | } 70 | { 71 | msg := &YMsg.S2S_rpc_msg{} 72 | msg.M_func_name = "Test_3" 73 | msg.M_func_parameter = make([][]byte, 0) 74 | { 75 | 76 | _bytes, _ := jsoniter.Marshal(1) 77 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 78 | } 79 | { 80 | _bytes, _ := jsoniter.Marshal("123") 81 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 82 | } 83 | YNode.RPCCall(msg) 84 | } 85 | { 86 | msg := &YMsg.S2S_rpc_msg{} 87 | msg.M_func_name = "Test_4" 88 | msg.M_func_parameter = make([][]byte, 0) 89 | { 90 | 91 | _bytes, _ := jsoniter.Marshal(&YMsg.TestParam{ 92 | 123, 93 | "TESTPARAMTER", 94 | []int{ 95 | 1, 2, 3, 4, 5, 6, 7, 96 | }, 97 | }) 98 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 99 | } 100 | YNode.RPCCall(msg) 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /examples/RPCExample/Logic/TestModule2/TestModule.go: -------------------------------------------------------------------------------- 1 | package TestModule2 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YModule" 6 | "github.com/yxinyi/YCServer/engine/YMsg" 7 | "github.com/yxinyi/YCServer/engine/YNode" 8 | ) 9 | 10 | type TestInfo struct { 11 | YModule.BaseInter 12 | } 13 | 14 | func NewInfo(node_ *YNode.Info, uid_ uint64) YModule.Inter { 15 | _info := &TestInfo{} 16 | _info.Info = YModule.NewInfo(node_) 17 | 18 | return _info 19 | } 20 | func (m *TestInfo) GetInfo() *YModule.Info { 21 | return m.Info 22 | } 23 | func (m *TestInfo) Init() { 24 | m.Info.Init(m) 25 | } 26 | 27 | func (m *TestInfo) Close() { 28 | 29 | } 30 | 31 | func (m *TestInfo) RPC_Test() { 32 | ylog.Info("TestModule2 RPC_Test") 33 | } 34 | 35 | func (m *TestInfo) RPC_Test_2(val_ uint32) { 36 | ylog.Info("TestModule2 RPC_Test_2 [%v]", val_) 37 | } 38 | 39 | func (m *TestInfo) RPC_Test_3(val_ uint32, str_ string) float64 { 40 | ylog.Info("TestModule2 RPC_Test_3 [%v] [%v]", val_, str_) 41 | m.Info.RPCCall(YMsg.ToAgent("TestModule"), "Test_3", val_+1, "从 Module2 发出") 42 | return 0.1241423523452342 43 | } 44 | -------------------------------------------------------------------------------- /examples/RPCExample/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/json-iterator/go" 6 | "github.com/yxinyi/YCServer/engine/BaseModule/NetModule" 7 | "github.com/yxinyi/YCServer/engine/YMsg" 8 | "github.com/yxinyi/YCServer/engine/YNode" 9 | "github.com/yxinyi/YCServer/examples/RPCExample/Logic/TestModule" 10 | "github.com/yxinyi/YCServer/examples/RPCExample/Logic/TestModule2" 11 | _ "net/http/pprof" 12 | ) 13 | 14 | func main() { 15 | flag.Parse() 16 | YNode.ModuleCreateFuncRegister("NetModule", NetModule.NewInfo) 17 | YNode.ModuleCreateFuncRegister("TestModule", TestModule.NewInfo) 18 | YNode.ModuleCreateFuncRegister("TestModule2", TestModule2.NewInfo) 19 | YNode.SetNodeID(0) 20 | YNode.Register( 21 | YNode.NewModuleInfo("NetModule",0), 22 | YNode.NewModuleInfo("TestModule", 0), 23 | YNode.NewModuleInfo("TestModule2", 0), 24 | ) 25 | { 26 | msg := &YMsg.S2S_rpc_msg{} 27 | msg.M_func_name = "Test_3" 28 | msg.M_tar.M_module_name = "TestModule" 29 | msg.M_func_parameter = make([][]byte, 0) 30 | { 31 | _bytes, _ := jsoniter.Marshal(1) 32 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 33 | } 34 | { 35 | _bytes, _ := jsoniter.Marshal("测试") 36 | msg.M_func_parameter = append(msg.M_func_parameter, _bytes) 37 | } 38 | YNode.RPCCall(msg) 39 | } 40 | YNode.Start() 41 | } 42 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Client/Client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hajimehoshi/ebiten/v2" 6 | "github.com/yxinyi/YCServer/engine/YNet" 7 | "github.com/yxinyi/YCServer/engine/YTool" 8 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 9 | "log" 10 | ) 11 | 12 | var g_client_cnn = YNet.NewConnect() 13 | var g_sync_queue = YTool.NewSyncQueue() 14 | 15 | func main() { 16 | fmt.Println("Client start") 17 | g_client_cnn.Connect("127.0.0.1:20000") 18 | g_client_cnn.Start() 19 | g_client_cnn.SendJson(Msg.C2S_Login{}) 20 | g_client_cnn.SendJson(Msg.C2S_FirstEnterMap{}) 21 | game, err := NewMainGame() 22 | if err != nil { 23 | log.Fatal(err.Error()) 24 | } 25 | ebiten.SetWindowSize(ScreenWidth, ScreenHeight) 26 | ebiten.SetWindowTitle("mmo aoi test") 27 | if err := ebiten.RunGame(game); err != nil { 28 | log.Fatal(err.Error()) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Client/Game.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/hajimehoshi/ebiten/v2" 5 | "github.com/yxinyi/YCServer/engine/YNet" 6 | "image/color" 7 | "math/rand" 8 | "time" 9 | ) 10 | 11 | func init() { 12 | rand.Seed(time.Now().UnixNano()) 13 | g_map.Init() 14 | } 15 | 16 | // Game represents a game state. 17 | type Game struct { 18 | } 19 | 20 | // NewGame generates a new Game object. 21 | func NewMainGame() (*Game, error) { 22 | g := &Game{ 23 | } 24 | var err error 25 | if err != nil { 26 | return nil, err 27 | } 28 | return g, nil 29 | } 30 | 31 | // Layout implements ebiten.Game's Layout. 32 | func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) { 33 | return ScreenWidth, ScreenHeight 34 | } 35 | 36 | // Update updates the current game state. 37 | func (g *Game) Update() error { 38 | 39 | for more := true; more; { 40 | select { 41 | case _net_msg := <-YNet.G_net_msg_chan: 42 | YNet.Dispatch(_net_msg.M_session, _net_msg.M_net_msg) 43 | default: 44 | more = false 45 | } 46 | } 47 | g_map.Update() 48 | return nil 49 | } 50 | 51 | // Draw draws the current game to the given screen. 52 | func (g *Game) Draw(screen *ebiten.Image) { 53 | screen.Fill(color.NRGBA{0x00, 0x40, 0x80, 0xff}) 54 | g_map.Draw(screen) 55 | } 56 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Client/Map.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/hajimehoshi/ebiten/v2" 6 | "github.com/hajimehoshi/ebiten/v2/ebitenutil" 7 | "github.com/hajimehoshi/ebiten/v2/inpututil" 8 | "github.com/hajimehoshi/ebiten/v2/text" 9 | "github.com/yxinyi/YCServer/engine/YNet" 10 | "github.com/yxinyi/YCServer/engine/YTool" 11 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 12 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Util" 13 | "golang.org/x/image/font" 14 | "golang.org/x/image/font/gofont/goregular" 15 | "golang.org/x/image/font/opentype" 16 | "image/color" 17 | "log" 18 | "math" 19 | ) 20 | 21 | const ( 22 | ScreenWidth = 1280 23 | ScreenHeight = 720 24 | userGridSize = 5 25 | ) 26 | 27 | var g_center_pos = YTool.PositionXY{float64(ScreenWidth / 2), float64(ScreenHeight / 2)} 28 | 29 | var uiFont font.Face 30 | 31 | type Map struct { 32 | m_user_list map[uint64]Msg.UserData 33 | } 34 | 35 | func NewMap() *Map { 36 | return &Map{ 37 | m_user_list: make(map[uint64]Msg.UserData), 38 | } 39 | } 40 | 41 | var g_map = NewMap() 42 | var g_main_uid uint64 43 | var g_main_path_node []YTool.PositionXY 44 | var g_main_check_node []YTool.PositionXY 45 | 46 | type MapMazeInfo struct { 47 | M_msg *Msg.S2C_AllSyncMapInfo 48 | *YTool.Rectangle 49 | M_grid_size float64 50 | } 51 | 52 | func NewMapMazeInfo(msg_ *Msg.S2C_AllSyncMapInfo) *MapMazeInfo { 53 | _info := &MapMazeInfo{} 54 | _info.M_msg = msg_ 55 | _info.Rectangle = YTool.NewRectangle() 56 | _up_down_offset, _left_right_offset := Util.MapOffDiff(0x7FFFFFFF<<32|0x7FFFFFFF, msg_.M_map_uid) 57 | 58 | _left_up := &YTool.PositionXY{ 59 | M_x: float64(_left_right_offset) * msg_.M_width, 60 | M_y: float64(_up_down_offset) * msg_.M_height, 61 | } 62 | _right_down := &YTool.PositionXY{ 63 | M_x: _left_up.M_x + msg_.M_width, 64 | M_y: _left_up.M_y + msg_.M_height, 65 | } 66 | _info.Rectangle.InitForLefUPRightDown(_left_up, _right_down) 67 | _info.M_grid_size = msg_.M_height / float64(len(msg_.M_maze)) 68 | return _info 69 | } 70 | 71 | var g_map_maze_info = make(map[uint64]*MapMazeInfo) 72 | 73 | func (m *Map) Init() { 74 | tt, err := opentype.Parse(goregular.TTF) 75 | if err != nil { 76 | log.Fatal(err.Error()) 77 | } 78 | uiFont, _ = opentype.NewFace(tt, &opentype.FaceOptions{ 79 | Size: 12, 80 | DPI: 72, 81 | Hinting: font.HintingFull, 82 | }) 83 | ebiten.SetMaxTPS(150) 84 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2C_FirstEnterMap) { 85 | m.UpdateUser(msg_.M_data) 86 | }) 87 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2C_AllSyncMapInfo) { 88 | g_map_maze_info[msg_.M_map_uid] = NewMapMazeInfo(&msg_) 89 | }) 90 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2C_Login) { 91 | g_main_uid = msg_.M_main_uid 92 | //m.AddNewUser(msg_.M_defalut_value) 93 | }) 94 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2C_MapAStarNodeUpdate) { 95 | g_main_path_node = msg_.M_path 96 | }) 97 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2CMapAddUser) { 98 | for _, _it := range msg_.M_user { 99 | m.AddNewUser(_it) 100 | } 101 | }) 102 | 103 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2CMapUpdateUser) { 104 | //atomic.AddInt32(&_msg_count, 1) 105 | for _, _it := range msg_.M_user { 106 | if _it.M_current_map_id != m.MainMapID() { 107 | fmt.Printf("b[%v]a[%v]\n", m.MainMapID(), _it.M_current_map_id) 108 | } 109 | fmt.Printf("pos[%v]\n", _it.M_pos.DebugString()) 110 | 111 | m.UpdateUser(_it) 112 | } 113 | }) 114 | YNet.Register(func(_ YNet.Session, msg_ Msg.S2CMapDeleteUser, ) { 115 | for _, _it := range msg_.M_user { 116 | m.DeleteUser(_it.M_uid) 117 | } 118 | }) 119 | 120 | } 121 | func (m *Map) DeleteUser(uid_ uint64) { 122 | delete(m.m_user_list, uid_) 123 | } 124 | 125 | func (m *Map) AddNewUser(user_data_ Msg.UserData) { 126 | m.m_user_list[user_data_.M_uid] = user_data_ 127 | } 128 | 129 | var g_slope string 130 | 131 | func (m *Map) UpdateUser(user_data_ Msg.UserData) { 132 | 133 | if g_main_uid == user_data_.M_uid { 134 | g_slope = fmt.Sprintf("%.2f", (user_data_.M_pos.M_y-m.m_user_list[user_data_.M_uid].M_pos.M_y)/(user_data_.M_pos.M_x-m.m_user_list[user_data_.M_uid].M_pos.M_x)) 135 | } 136 | m.m_user_list[user_data_.M_uid] = user_data_ 137 | } 138 | 139 | func (m *Map) UserMove(msg_ Msg.S2C_MOVE, _ YNet.Session) { 140 | m.m_user_list[msg_.M_uid] = msg_.M_data 141 | } 142 | 143 | func (m *Map) MainMapID() uint64 { 144 | return m.m_user_list[g_main_uid].M_current_map_id 145 | } 146 | 147 | func (m *Map) MainMapInfo() *MapMazeInfo { 148 | return g_map_maze_info[m.MainMapID()] 149 | } 150 | 151 | func (m *Map) MainPos() YTool.PositionXY { 152 | return m.m_user_list[g_main_uid].M_pos 153 | } 154 | 155 | func (m *Map) Update() { 156 | if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { 157 | _tar_x, _tar_y := ebiten.CursorPosition() 158 | _x_diff := float64(_tar_x) - g_center_pos.M_x 159 | _y_diff := float64(_tar_y) - g_center_pos.M_y 160 | 161 | _fix_tar_pos := &YTool.PositionXY{ 162 | m.MainPos().M_x + _x_diff, 163 | m.MainPos().M_y + _y_diff, 164 | } 165 | 166 | _tar_map := uint64(0) 167 | 168 | _msg := Msg.C2S_UserMove{ 169 | _tar_map, 170 | *_fix_tar_pos, 171 | } 172 | //fmt.Printf("[%v]", _msg.M_tar_map_uid) 173 | g_client_cnn.SendJson(_msg) 174 | } 175 | 176 | if inpututil.IsKeyJustPressed(ebiten.KeyT) { 177 | switch ebiten.MaxTPS() { 178 | case 30: 179 | ebiten.SetMaxTPS(60) 180 | case 60: 181 | ebiten.SetMaxTPS(90) 182 | case 90: 183 | ebiten.SetMaxTPS(120) 184 | case 120: 185 | ebiten.SetMaxTPS(150) 186 | case 150: 187 | ebiten.SetMaxTPS(30) 188 | } 189 | 190 | } 191 | } 192 | 193 | func (m *Map) InViewRange(pos YTool.PositionXY) bool { 194 | _distance := pos.GetOffset(m.MainPos()) 195 | if math.Abs(_distance.M_x) > ScreenWidth/2-10 || math.Abs(_distance.M_y) > ScreenHeight/2-10 { 196 | return false 197 | } 198 | return true 199 | } 200 | 201 | func (m *Map) PosConvert(pos YTool.PositionXY) YTool.PositionXY { 202 | 203 | _main_user_pos := m.MainPos() 204 | 205 | _x_diff := g_center_pos.M_x - _main_user_pos.M_x 206 | _y_diff := g_center_pos.M_y - _main_user_pos.M_y 207 | pos.M_x += _x_diff 208 | pos.M_y += _y_diff 209 | return pos 210 | } 211 | 212 | func (m *Map) Draw(screen *ebiten.Image) { 213 | //fmt.Printf("[%v] [%v]\n", m.MainMapID(), m.MainPos().DebugString()) 214 | _is_show := 0 215 | _show_pos := YTool.PositionXY{} 216 | 217 | _round_map := make(map[uint64]struct{}) 218 | _round_map[m.MainMapID()] = struct{}{} 219 | _round_map[m.MainMapID()-1] = struct{}{} 220 | _round_map[m.MainMapID()+1] = struct{}{} 221 | _round_map[m.MainMapID()+(1<<32)] = struct{}{} 222 | _round_map[m.MainMapID()-(1<<32)] = struct{}{} 223 | _round_map[m.MainMapID()+(1<<32)+1] = struct{}{} 224 | _round_map[m.MainMapID()+(1<<32)-1] = struct{}{} 225 | _round_map[m.MainMapID()-(1<<32)+1] = struct{}{} 226 | _round_map[m.MainMapID()-(1<<32)-1] = struct{}{} 227 | 228 | for _map_uid_it := range _round_map { 229 | _map_it := g_map_maze_info[_map_uid_it] 230 | if _map_it == nil{ 231 | continue 232 | } 233 | for _row_idx_it, _row_it := range _map_it.M_msg.M_maze { 234 | _row_idx := _row_idx_it 235 | for _col_idx_it, _block_val := range _row_it { 236 | _col_idx := _col_idx_it 237 | if _block_val != 0 { 238 | _block_pos := YTool.PositionXY{float64(_col_idx)*_map_it.M_grid_size + _map_it.LeftUp.M_x, float64(_row_idx)*_map_it.M_grid_size + _map_it.LeftUp.M_y} 239 | if !m.InViewRange(_block_pos) { 240 | continue 241 | } 242 | _block_pos = m.PosConvert(_block_pos) 243 | if _map_it.M_msg.M_map_uid == 9223372034707292159 && _is_show == 0 { 244 | _is_show = 1 245 | _show_pos = _block_pos 246 | } 247 | _rgb := color.RGBA{ 248 | uint8(((_map_it.M_msg.M_map_uid>>32)+100-0x7fffffff)*77) & 0xff, 249 | uint8(((_map_it.M_msg.M_map_uid)+133-0x7fffffff)*155) & 0xff, 250 | uint8(((_map_it.M_msg.M_map_uid>>32)+211-0x7fffffff)*211) & 0xff, 251 | 0xff, 252 | } 253 | /* if _row_idx == 0 && _col_idx == 0 { 254 | fmt.Printf("first block [%v]\n", _block_pos.DebugString()) 255 | }*/ 256 | ebitenutil.DrawRect(screen, _block_pos.M_x, _block_pos.M_y, _map_it.M_grid_size, _map_it.M_grid_size, _rgb) 257 | } 258 | } 259 | } 260 | } 261 | gridSize := float64(10) 262 | for _, it := range m.m_user_list { 263 | for _, path_it := range it.M_path { 264 | if !m.InViewRange(path_it) { 265 | continue 266 | } 267 | _path_pos := m.PosConvert(path_it) 268 | ebitenutil.DrawRect(screen, _path_pos.M_x, _path_pos.M_y, gridSize, gridSize, color.RGBA{0xff, 0x00, 0x00, 0xff}) 269 | } 270 | } 271 | 272 | for _, path_it := range g_main_path_node { 273 | if !m.InViewRange(path_it) { 274 | continue 275 | } 276 | _path_pos := m.PosConvert(path_it) 277 | ebitenutil.DrawRect(screen, _path_pos.M_x, _path_pos.M_y, gridSize, gridSize, color.RGBA{0xff, 0x00, 0x00, 0xff}) 278 | } 279 | 280 | for _uid_it, it := range m.m_user_list { 281 | if !m.InViewRange(it.M_pos) { 282 | continue 283 | } 284 | /* if m.m_user_list[_uid_it].M_pos.Distance(it.M_pos) > 100 { 285 | panic("1") 286 | }*/ 287 | 288 | if g_main_uid == _uid_it { 289 | //detailStr := fmt.Sprintf("%.2f,%.2f", it.M_pos.M_x, it.M_pos.M_y) 290 | //text.Draw(screen, detailStr, uiFont, int(it.M_pos.M_x), int(it.M_pos.M_y+20), color.White) 291 | _main_user := m.PosConvert(YTool.PositionXY{it.M_pos.M_x + (gridSize-userGridSize)/2, it.M_pos.M_y + (gridSize-userGridSize)/2}) 292 | ebitenutil.DrawRect(screen, _main_user.M_x, _main_user.M_y, userGridSize, userGridSize, color.RGBA{0xff, 0xa0, 0x00, 0xff}) 293 | } else { 294 | //detailStr := fmt.Sprintf("%.2f,%.2f", it.M_pos.M_x, it.M_pos.M_y) 295 | //text.Draw(screen, detailStr, uiFont, int(it.M_pos.M_x), int(it.M_pos.M_y+20), color.White) 296 | _main_user := m.PosConvert(YTool.PositionXY{it.M_pos.M_x + (gridSize-userGridSize)/2, it.M_pos.M_y + (gridSize-userGridSize)/2}) 297 | ebitenutil.DrawRect(screen, _main_user.M_x, _main_user.M_y, userGridSize, userGridSize, color.RGBA{0x80, 0xa0, 0x00, 0xff}) 298 | //ebitenutil.DrawRect(screen, it.M_pos.M_x+(gridSize-userGridSize)/2, it.M_pos.M_y+(gridSize-userGridSize)/2, userGridSize, userGridSize, color.RGBA{0x80, 0xa0, 0xc0, 0xff}) 299 | } 300 | } 301 | 302 | ebitenutil.DebugPrint(screen, fmt.Sprintf("MAX: %d\nTPS: %0.2f\nFPS: %0.2f \nCM[%v]\nPOS[%v] \nFP[%v]", ebiten.MaxTPS(), ebiten.CurrentTPS(), ebiten.CurrentFPS(), m.MainMapID(), m.MainPos().DebugString(), _show_pos.DebugString())) 303 | 304 | { 305 | detailStr := fmt.Sprintf("%d,%d", 100, 100) 306 | text.Draw(screen, detailStr, uiFont, int(100), int(100), color.White) 307 | } 308 | { 309 | detailStr := fmt.Sprintf("%d,%d", 400, 100) 310 | text.Draw(screen, detailStr, uiFont, int(400), int(100), color.White) 311 | } 312 | { 313 | detailStr := fmt.Sprintf("%d,%d", 100, 400) 314 | text.Draw(screen, detailStr, uiFont, int(100), int(400), color.White) 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Msg/msg.go: -------------------------------------------------------------------------------- 1 | package Msg 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | ) 6 | 7 | type Message struct { 8 | Id int 9 | Number int 10 | } 11 | 12 | type UserData struct { 13 | M_uid uint64 14 | M_current_map_id uint64 15 | M_pos YTool.PositionXY 16 | M_path []YTool.PositionXY 17 | } 18 | 19 | type C2SUserMove struct { 20 | M_pos YTool.PositionXY 21 | } 22 | 23 | type S2C_MOVE struct { 24 | M_uid uint64 25 | M_data UserData 26 | } 27 | 28 | type S2CMapFullSync struct { 29 | M_user []UserData 30 | } 31 | 32 | type S2CMapAddUser struct { 33 | M_user []UserData 34 | } 35 | type S2CMapUpdateUser struct { 36 | M_user []UserData 37 | } 38 | type S2CMapDeleteUser struct { 39 | M_user []UserData 40 | } 41 | type S2C_MapAStarNodeUpdate struct { 42 | M_uid uint64 43 | M_path []YTool.PositionXY 44 | } 45 | 46 | type C2S_Login struct { 47 | } 48 | 49 | type S2C_Login struct { 50 | M_main_uid uint64 51 | //M_defalut_value UserData 52 | } 53 | 54 | type C2S_FirstEnterMap struct { 55 | } 56 | 57 | type S2C_FirstEnterMap struct { 58 | M_data UserData 59 | } 60 | 61 | type S2C_AllSyncMapInfo struct { 62 | M_map_uid uint64 63 | M_maze [][]float64 64 | M_height float64 65 | M_width float64 66 | M_overlap float64 67 | M_gird_size float64 68 | } 69 | 70 | type C2S_UserMove struct { 71 | M_tar_map_uid uint64 72 | M_pos YTool.PositionXY 73 | } 74 | 75 | type MapLoad struct { 76 | M_map_uid uint64 77 | M_load uint32 78 | } 79 | 80 | type S2S_SyncOtherNodeRPC struct { 81 | M_net_msg []string 82 | M_node_id uint32 83 | } 84 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Logic/Move/MoveControl.go: -------------------------------------------------------------------------------- 1 | package move 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YTool" 5 | "time" 6 | ) 7 | 8 | type MoveControl struct { 9 | M_pos YTool.PositionXY 10 | M_tar YTool.PositionXY 11 | M_next_path YTool.PositionXY 12 | M_speed float64 13 | M_last_move_time time.Time 14 | M_view_range float64 15 | M_path_cur_idx int 16 | M_path []YTool.PositionXY 17 | } 18 | 19 | func (c *MoveControl) CanToNextPath() bool { 20 | if c.M_path_cur_idx == len(c.M_path) { 21 | return false 22 | } 23 | 24 | return true 25 | } 26 | 27 | func (c *MoveControl) DebugString() string { 28 | _str := "" 29 | /* for _idx := 0; _idx < c.M_path_queue.Length(); _idx++ { 30 | _str += c.M_path_queue.Get(_idx).(YTool.PositionXY).DebugString() 31 | }*/ 32 | 33 | return _str 34 | } 35 | 36 | func (c *MoveControl) ClearPathNode() { 37 | c.M_path = c.M_path[:0] 38 | } 39 | 40 | func (c *MoveControl) GetPathNode() []YTool.PositionXY { 41 | return c.M_path 42 | } 43 | 44 | func (c *MoveControl) toNextPath() { 45 | c.M_next_path = c.M_path[c.M_path_cur_idx] 46 | c.M_path_cur_idx++ 47 | } 48 | 49 | func (c *MoveControl) MoveQueue(path_queue_ *YTool.Queue) { 50 | c.M_path_cur_idx = 0 51 | _path_node := make([]YTool.PositionXY, 0) 52 | for _idx := 0; _idx < path_queue_.Length(); _idx++ { 53 | _path_node = append(_path_node, path_queue_.Get(_idx).(YTool.PositionXY)) 54 | } 55 | c.M_path = _path_node 56 | c.toNextPath() 57 | } 58 | func (c *MoveControl) MoveTarget(tar_ YTool.PositionXY) { 59 | c.M_tar = tar_ 60 | } 61 | 62 | func (c *MoveControl) MoveUpdate(time_ time.Time) bool { 63 | defer func() { 64 | c.M_last_move_time = time_ 65 | }() 66 | 67 | if c.M_pos.IsSame(c.M_tar) { 68 | return false 69 | } 70 | 71 | if c.M_pos.IsSame(c.M_next_path) { 72 | if !c.CanToNextPath() { 73 | return false 74 | } 75 | c.toNextPath() 76 | } 77 | 78 | _distance := c.M_pos.Distance(&c.M_next_path) 79 | 80 | _interval_time := time_.Sub(c.M_last_move_time).Seconds() 81 | _this_move_distance := _interval_time * c.M_speed 82 | 83 | if _distance < _this_move_distance { 84 | c.M_pos = c.M_next_path 85 | return true 86 | } 87 | _precent := _this_move_distance / _distance 88 | 89 | _distance_pos := c.M_pos.GetOffset(c.M_next_path) 90 | 91 | c.M_pos.M_x += _distance_pos.M_x * _precent 92 | c.M_pos.M_y += _distance_pos.M_y * _precent 93 | 94 | return true 95 | } 96 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/pflag" 6 | "github.com/spf13/viper" 7 | "github.com/yxinyi/YCServer/engine/YConfig" 8 | "github.com/yxinyi/YCServer/engine/YModule" 9 | "github.com/yxinyi/YCServer/engine/YMsg" 10 | "github.com/yxinyi/YCServer/engine/YNode" 11 | "github.com/yxinyi/YCServer/engine/YTool" 12 | "log" 13 | "net/http" 14 | _ "net/http/pprof" 15 | ) 16 | 17 | type NodeCfg struct { 18 | NodeID uint32 19 | Port uint32 20 | PprofPort uint32 21 | Modules []string 22 | } 23 | type NodeCfgList struct { 24 | CfgList []NodeCfg 25 | } 26 | 27 | func init() { 28 | pflag.Uint("NodeID", 0, "服务器ID") 29 | } 30 | func main() { 31 | pflag.Parse() 32 | ModuleCreateFuncLoad() 33 | viper.BindPFlags(pflag.CommandLine) 34 | 35 | YNode.SetNodeID(viper.GetUint32("NodeID")) 36 | 37 | var _node_cfg_list NodeCfgList 38 | YConfig.Load("node_cfg.json", &_node_cfg_list) 39 | YTool.JsonPrint(_node_cfg_list) 40 | 41 | YNode.Register( 42 | YNode.NewModuleInfo("NetModule",0), 43 | ) 44 | for _, _node_it := range _node_cfg_list.CfgList { 45 | if _node_it.NodeID == viper.GetUint32("NodeID") { 46 | _listen_addr := fmt.Sprintf("0.0.0.0:%d", _node_it.Port) 47 | YNode.RPCCall(YModule.NewRPCMsg(YMsg.ToAgent("NetModule"), "Listen", _listen_addr)) 48 | go func() { 49 | log.Fatal(http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", _node_it.PprofPort), nil)) 50 | }() 51 | for _, _module_it := range _node_it.Modules { 52 | YNode.RPCCall(YModule.NewRPCMsg(YMsg.ToAgent("YNode", uint64(_node_it.NodeID)), "NewModule", _module_it, 0)) 53 | } 54 | } else { 55 | _connect_port := fmt.Sprintf("127.0.0.1:%d", _node_it.Port) 56 | YNode.RPCCall(YModule.NewRPCMsg(YMsg.ToAgent("NetModule"), "Connect", _connect_port)) 57 | YNode.RegisterNodeIpStr2NodeId(_connect_port, _node_it.NodeID) 58 | } 59 | } 60 | YNode.Start() 61 | } 62 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Module/Map/API.go: -------------------------------------------------------------------------------- 1 | package Map 2 | 3 | import ( 4 | ylog "github.com/yxinyi/YCServer/engine/YLog" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YTool" 7 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 8 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Module/UserManager" 9 | "time" 10 | ) 11 | 12 | func (m *Info) RPC_UserEnterMap(user_ UserManager.User) { 13 | _user := &User{ 14 | M_uid: user_.M_uid, 15 | M_current_map: user_.M_current_map, 16 | M_session_id: user_.M_session_id, 17 | } 18 | m.M_user_pool[_user.M_uid] = _user 19 | _user.M_current_map = m.m_map_uid 20 | _user.M_speed = 100 21 | _user.M_view_range = 1000 22 | 23 | m.randPos(_user) 24 | m.Info.SendNetMsgJson(_user.GetSessionID(), Msg.S2C_FirstEnterMap{ 25 | _user.ToClientJson(), 26 | }) 27 | 28 | m.RPC_SyncMapInfoToClient(_user.GetSessionID()) 29 | 30 | m.m_aoi.Enter(_user.M_uid, _user.GetMoveControl().M_view_range, _user.GetMoveControl().M_pos) 31 | //负载均衡同步 32 | m.NotifyMapLoad() 33 | } 34 | 35 | func (m *Info) RPC_SyncMapInfoToClient(s_ uint64) { 36 | _msg := Msg.S2C_AllSyncMapInfo{ 37 | m.m_map_uid, 38 | make([][]float64, int(m.m_vaild_col_grid)), 39 | m.m_vaild_height, 40 | m.m_vaild_width, 41 | float64(m.m_overlap_count), 42 | m.m_gird_size, 43 | } 44 | 45 | _col_loop := 0 46 | for _col_idx := int(m.m_overlap_count); _col_idx < int(m.m_overlap_count+m.m_vaild_col_grid); _col_idx++ { 47 | _msg.M_maze[_col_loop] = make([]float64, int(m.m_vaild_row_grid)) 48 | _row_loop := 0 49 | for _row_idx := int(m.m_overlap_count); _row_idx < int(m.m_overlap_count+m.m_vaild_row_grid); _row_idx++ { 50 | _msg.M_maze[_col_loop][_row_loop] = m.m_go_astar.GetMaze()[_col_idx][_row_idx] 51 | _row_loop++ 52 | } 53 | _col_loop++ 54 | } 55 | 56 | m.Info.SendNetMsgJson(s_, _msg) 57 | } 58 | 59 | func (m *Info) RPC_UserQuitMap(user_ UserManager.User) { 60 | delete(m.M_user_pool, user_.M_uid) 61 | user_.M_current_map = 0 62 | //负载均衡同步 63 | m.NotifyMapLoad() 64 | } 65 | 66 | func (m *Info) RPC_UserMove(user_uid_ uint64, move_msg_ Msg.C2S_UserMove) { 67 | 68 | //_map_pos := m.ClientPosConvertMapPos(move_msg_.M_pos) 69 | _map_pos := move_msg_.M_pos 70 | _map_pos.M_x = float64(int(_map_pos.M_x)) 71 | _map_pos.M_y = float64(int(_map_pos.M_y)) 72 | if m.m_go_astar.IsBlock(m.MapPosConvertMapIdx(_map_pos)) { 73 | return 74 | } 75 | if m.isGhostUser(user_uid_) { 76 | return 77 | } 78 | 79 | _user, exists := m.M_user_pool[user_uid_] 80 | if !exists { 81 | return 82 | } 83 | if _user.M_map_swtich_state == UserManager.CONST_MAP_SWITCHING { 84 | return 85 | } 86 | ylog.Info("[RPC_UserMove] tar [%v]", _map_pos.DebugString()) 87 | _user.MoveTarget(_map_pos) 88 | 89 | m.m_go_astar.Search(m.MapPosConvertMapIdx(_user.M_pos), m.MapPosConvertMapIdx(_user.M_tar), func(path_ []int) { 90 | _user, exists := m.M_user_pool[_user.M_uid] 91 | if !exists { 92 | return 93 | } 94 | if len(path_) == 0 { 95 | return 96 | } 97 | _target_indx := m.MapPosConvertMapIdx(_user.M_tar) 98 | if path_[len(path_)-1] != _target_indx { 99 | return 100 | } 101 | _path_idx := m.IdxListConvertPosList(path_) 102 | 103 | _path_pos := make([]YTool.PositionXY, 0, len(path_)) 104 | for _, _it := range path_ { 105 | _path_pos = append(_path_pos, m.MapIdxConvertMapPos(_it)) 106 | } 107 | 108 | _user.GetMoveControl().MoveQueue(_path_idx) 109 | m.Info.SendNetMsgJson(_user.GetSessionID(), Msg.S2C_MapAStarNodeUpdate{ 110 | _user.M_uid, 111 | _path_pos, 112 | }) 113 | }) 114 | } 115 | 116 | func (m *Info) RPC_RegisterNeighborMap(neighbor_map_list_ []uint64) { 117 | for _, _map_id := range neighbor_map_list_ { 118 | _, _exists := m.m_neighbor_uid[_map_id] 119 | if !_exists { 120 | m.m_neighbor_uid[_map_id] = struct{}{} 121 | //判断邻居位于本地图的哪个位置,发送边缘N行或N列地图作为缓冲切换边缘 122 | //暂时固定发送10行 123 | var _sync_map_info [][]float64 124 | _sync_line_count := 10 125 | _col_start_index, _col_end_index, _row_start_index, _row_end_index := m.MapSyncOverlapColRowRange(_map_id) 126 | _sync_map_info = make([][]float64, _col_end_index-_col_start_index+1) 127 | _col_set_idx := 0 128 | for _col_idx := _col_start_index; _col_idx < _col_end_index; _col_idx++ { 129 | _row_line_info := make([]float64, _row_end_index-_row_start_index+1) 130 | _row_set_idx := 0 131 | for _row_idx := _row_start_index; _row_idx < _row_end_index; _row_idx++ { 132 | _row_line_info[_row_set_idx] = m.m_go_astar.GetMaze()[_col_idx][_row_idx] 133 | _row_set_idx++ 134 | } 135 | _sync_map_info[_col_set_idx] = _row_line_info 136 | _col_set_idx++ 137 | } 138 | m.Info.RPCCall(YMsg.ToAgent("Map", _map_id), "SyncOverlapBlock", _sync_map_info, m.m_map_uid, _sync_line_count) 139 | } 140 | } 141 | } 142 | 143 | func (m *Info) RPC_SyncOverlapBlock(overlap_map_info_ [][]float64, over_map_uid_ uint64, over_map_line_ int) { 144 | _col_start_index, _col_end_index, _row_start_index, _row_end_index := m.MapSetOverlapColRowRange(over_map_uid_) 145 | _col_get_idx := 0 146 | for _col_idx := _col_start_index; _col_idx < _col_end_index; _col_idx++ { 147 | _row_get_idx := 0 148 | for _row_idx := _row_start_index; _row_idx < _row_end_index; _row_idx++ { 149 | m.m_go_astar.GetMaze()[_col_idx][_row_idx] = overlap_map_info_[_col_get_idx][_row_get_idx] 150 | _row_get_idx++ 151 | } 152 | _col_get_idx++ 153 | } 154 | 155 | for _, _it := range m.M_user_pool { 156 | m.RPC_SyncMapInfoToClient(_it.M_session_id) 157 | } 158 | } 159 | func (m *Info) RPC_UserConvertToThisMap(user_uid_ uint64) { 160 | _user := m.M_user_pool[user_uid_] 161 | _user.M_current_map = m.m_map_uid 162 | _user.M_last_move_time = time.Now() 163 | _user.M_map_swtich_state = UserManager.CONST_MAP_SWITCH_NONE 164 | { 165 | _update_msg := Msg.S2CMapUpdateUser{ 166 | M_user: make([]Msg.UserData, 0), 167 | } 168 | _update_msg.M_user = append(_update_msg.M_user, _user.ToClientJson()) 169 | m.SendNetMsgJson(_user.M_session_id, _update_msg) 170 | } 171 | m.m_aoi.Move(_user.M_uid, _user.M_pos) 172 | m.Info.RPCCall(YMsg.ToAgent("UserManager"), "UserChangeCurrentMap", user_uid_, _user.M_current_map) 173 | m.Info.RPCCall(YMsg.ToAgent("UserManager"), "UserFinishSwitchMap", user_uid_) 174 | 175 | } 176 | func (m *Info) RPC_SyncGhostUser(user_ User) { 177 | _, exists := m.M_user_pool[user_.M_uid] 178 | if !exists { 179 | m.RPC_SyncMapInfoToClient(user_.M_session_id) 180 | m.m_aoi.Enter(user_.M_uid, user_.M_view_range, user_.M_pos) 181 | m.M_user_pool[user_.M_uid] = &user_ 182 | } else { 183 | m.m_aoi.Move(user_.M_uid, user_.M_pos) 184 | m.M_user_pool[user_.M_uid] = &user_ 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Module/Map/Info.go: -------------------------------------------------------------------------------- 1 | package Map 2 | 3 | import ( 4 | aoi "github.com/yxinyi/YCServer/engine/YAoi" 5 | "github.com/yxinyi/YCServer/engine/YAttr" 6 | "github.com/yxinyi/YCServer/engine/YEntity" 7 | "github.com/yxinyi/YCServer/engine/YJson" 8 | ylog "github.com/yxinyi/YCServer/engine/YLog" 9 | "github.com/yxinyi/YCServer/engine/YModule" 10 | "github.com/yxinyi/YCServer/engine/YPathFinding" 11 | "github.com/yxinyi/YCServer/engine/YTool" 12 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 13 | move "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Logic/Move" 14 | "time" 15 | ) 16 | 17 | func init() { 18 | YEntity.RegisterEntityAttr("User", 19 | YAttr.Define("MapInfo", 20 | YAttr.Tmpl("CurrentMap", uint64(0), true, true, true, true), 21 | YAttr.Tmpl("SessionID", uint64(0), true, true, true, true), 22 | YAttr.Tmpl("MapSwitchState", uint64(0), true, true, true, true), 23 | YAttr.Tmpl("MoveControl", move.MoveControl{}, true, true, true, true), 24 | ), 25 | ) 26 | } 27 | 28 | type User struct { 29 | //*YEntity.Info 30 | M_uid uint64 `SA:""SG:""SC:"-"C:""` 31 | M_current_map uint64 `SA:""SG:""SC:""C:"-"` 32 | M_session_id uint64 `SA:""SC:""C:"-"` 33 | M_map_swtich_state uint32 `SA:""SG:""SC:"-"C:""` 34 | move.MoveControl `SG:"move"` 35 | } 36 | 37 | func (u *User) GetMoveControl() *move.MoveControl { 38 | return &u.MoveControl 39 | } 40 | func (u *User) GetCurrentMap() uint64 { 41 | return u.M_current_map 42 | } 43 | func (u *User) GetSessionID() uint64 { 44 | return u.M_session_id 45 | } 46 | func (u *User) GetMapSwitchState() uint64 { 47 | return u.GetMapSwitchState() 48 | } 49 | 50 | func (u *User) ToGhost() User { 51 | _ghost_u := User{} 52 | _ghost_user_str, _err := YJson.GhostMarshal(*u) 53 | if _err!= nil { 54 | ylog.Erro("[%v]",_err.Error()) 55 | } 56 | YJson.UnMarshal(_ghost_user_str, &_ghost_u) 57 | return _ghost_u 58 | } 59 | 60 | /*func (u *User) GetMoveControl() *move.MoveControl { 61 | return u.GetAttr("MapInfo.MoveControl").(*move.MoveControl) 62 | } 63 | 64 | func (u *User) GetCurrentMap() uint64 { 65 | return *u.GetAttr("MapInfo.CurrentMap").(*uint64) 66 | } 67 | func (u *User) GetSessionID() uint64 { 68 | return *u.GetAttr("MapInfo.SessionID").(*uint64) 69 | } 70 | func (u *User) GetMapSwitchState() uint64 { 71 | return *u.GetAttr("MapInfo.MapSwitchState").(*uint64) 72 | }*/ 73 | 74 | func (u *User) MoveUpdate(time_ time.Time) bool { 75 | 76 | return u.GetMoveControl().MoveUpdate(time_) 77 | } 78 | 79 | func (u *User) ToClientJson() Msg.UserData { 80 | _user_msg := Msg.UserData{ 81 | M_uid: u.M_uid, 82 | M_current_map_id: u.GetCurrentMap(), 83 | M_pos: u.GetMoveControl().M_pos, 84 | } 85 | return _user_msg 86 | } 87 | 88 | type Info struct { 89 | YModule.BaseInter 90 | m_aoi *aoi.GoTowerAoiCellManager 91 | M_user_pool map[uint64]*User 92 | m_map_uid uint64 93 | m_go_astar *YPathFinding.AStarManager 94 | m_neighbor_uid map[uint64]struct{} 95 | 96 | m_gird_size float64 97 | 98 | m_vaild_up_left_pos YTool.PositionXY 99 | m_vaild_up_right_pos YTool.PositionXY 100 | m_vaild_down_left_pos YTool.PositionXY 101 | m_vaild_down_right_pos YTool.PositionXY 102 | m_origin_up_left_pos YTool.PositionXY 103 | m_origin_up_right_pos YTool.PositionXY 104 | m_origin_down_left_pos YTool.PositionXY 105 | m_origin_down_right_pos YTool.PositionXY 106 | m_vaild_width float64 107 | m_vaild_height float64 108 | m_total_width float64 109 | m_total_height float64 110 | m_vaild_row_grid float64 111 | m_vaild_col_grid float64 112 | m_total_row_grid float64 113 | m_total_col_grid float64 114 | m_overlap_count float64 115 | m_overlap_length float64 116 | 117 | m_up_down_offset int 118 | m_left_right_offset int 119 | } 120 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Module/MapManager/MapManager.go: -------------------------------------------------------------------------------- 1 | package MapManager 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YModule" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 8 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Module/UserManager" 9 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Util" 10 | "math" 11 | ) 12 | 13 | type Info struct { 14 | YModule.BaseInter 15 | M_map_pool map[uint64]Msg.MapLoad 16 | } 17 | 18 | func NewInfo(node_ *YNode.Info, uid uint64) YModule.Inter { 19 | _info := &Info{ 20 | M_map_pool: make(map[uint64]Msg.MapLoad), 21 | } 22 | _info.Info = YModule.NewInfo(node_) 23 | return _info 24 | } 25 | func (i *Info) Init() { 26 | i.Info.Init(i) 27 | } 28 | 29 | func (i *Info) Close() { 30 | } 31 | 32 | func (i *Info) RPC_MapRegister(load_ Msg.MapLoad) { 33 | i.M_map_pool[load_.M_map_uid] = load_ 34 | } 35 | 36 | func (i *Info) RPC_MapLoadChange(load_ Msg.MapLoad) { 37 | i.M_map_pool[load_.M_map_uid] = load_ 38 | } 39 | 40 | func (i *Info) GetLeastLoadMap() uint64 { 41 | _max_load := uint32(math.MaxUint32) 42 | _tar_map := uint64(0) 43 | for _, _it := range i.M_map_pool { 44 | if _it.M_load < _max_load { 45 | _max_load = _it.M_load 46 | } 47 | _tar_map = _it.M_map_uid 48 | } 49 | return _tar_map 50 | } 51 | 52 | const ( 53 | FirstMapUID = 0x7FFFFFFF<<32 | 0x7FFFFFFF 54 | ) 55 | 56 | func (i *Info) RPC_FirstEnterMap(user_ UserManager.User) { 57 | if len(i.M_map_pool) == 0 { 58 | i.RegisterModule("NewMap", FirstMapUID) 59 | } 60 | 61 | i.Info.RPCCall(YMsg.ToAgent("Map", FirstMapUID), "UserEnterMap", user_) 62 | i.Info.RPCCall(YMsg.ToAgent("UserManager"), "UserChangeCurrentMap", user_.M_uid, FirstMapUID) 63 | } 64 | 65 | func (i *Info) RPC_CreateMap(map_uid_ uint64) { 66 | _, exists := i.M_map_pool[map_uid_] 67 | if exists { 68 | return 69 | } 70 | i.M_map_pool[map_uid_] = Msg.MapLoad{} 71 | 72 | i.RegisterModule("NewMap", map_uid_) 73 | { 74 | _round_list := Util.GetRoundNeighborMapIDList(map_uid_) 75 | _exists_round := make([]uint64, 0) 76 | for _, _round_it := range _round_list { 77 | _, exists := i.M_map_pool[_round_it] 78 | if exists { 79 | _exists_round = append(_exists_round, _round_it) 80 | i.Info.RPCCall(YMsg.ToAgent("Map", _round_it), "RegisterNeighborMap", []uint64{map_uid_}) 81 | } 82 | } 83 | i.Info.RPCCall(YMsg.ToAgent("Map", map_uid_), "RegisterNeighborMap", _exists_round) 84 | } 85 | 86 | //RegisterNeighborMap 87 | } 88 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Module/README.md: -------------------------------------------------------------------------------- 1 | ### 登录及进入地图 2 | 3 | ```sequence 4 | Cli->UserMgr:MSG_C2S_Login 5 | Cli->UserMgr:MSG_C2S_EnterMap 6 | UserMgr->MapMgr:RPC_GetLeastLoadMap 7 | MapMgr->UserMgr:RPC_GetLeastLoadMap 8 | UserMgr->Map:RPC_UserEnterMap 9 | Map->UserMgr:RPC_UserEnterMap 10 | Map->UserMgr:RPC_MapLoadChange 11 | UserMgr->Cli:MSG_S2C_Login 12 | ``` 13 | 14 | ### 移动 15 | 16 | ```sequence 17 | Cli->UserMgr:MSG_C2S_UserMove 18 | UserMgr->Map:RPC_UserMove 19 | Map->UserMgr:RPC_UserMove 20 | UserMgr->Cli:MSG_S2C_UserMove 21 | ``` 22 | 23 | ### 玩家移动并且通知视野内玩家 24 | 25 | ```sequence 26 | Cli->UserMgr:MSG_C2S_UserMove 27 | UserMgr->Map:RPC_UserMove 28 | Map->OtherClis_1:MSG_S2C_EntityChange 29 | Map->OtherClis_2:MSG_S2C_EntityChange 30 | Map->OtherClis_3:MSG_S2C_EntityChange 31 | Map->UserMgr:RPC_UserMove 32 | UserMgr->Cli:MSG_S2C_UserMove 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Module/UserManager/UserInfo.go: -------------------------------------------------------------------------------- 1 | package UserManager 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YEntity" 5 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 6 | ) 7 | 8 | const ( 9 | CONST_MAP_SWITCH_NONE = iota 10 | CONST_MAP_SWITCHING 11 | ) 12 | 13 | type User struct { 14 | YEntity.Info 15 | M_uid uint64 16 | M_current_map uint64 17 | M_session_id uint64 18 | M_map_swtich_state uint32 19 | } 20 | 21 | func NewUser(uid_ uint64, session_id_ uint64) *User { 22 | return &User{ 23 | M_uid: uid_, 24 | M_session_id: session_id_, 25 | } 26 | } 27 | 28 | func (u *User) ToClientJson() Msg.UserData { 29 | _user_msg := Msg.UserData{ 30 | M_uid: u.M_uid, 31 | M_current_map_id: u.M_current_map, 32 | } 33 | return _user_msg 34 | } 35 | 36 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Module/UserManager/UserManager.go: -------------------------------------------------------------------------------- 1 | package UserManager 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/YModule" 5 | "github.com/yxinyi/YCServer/engine/YMsg" 6 | "github.com/yxinyi/YCServer/engine/YNode" 7 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Msg" 8 | ) 9 | 10 | type Info struct { 11 | YModule.BaseInter 12 | M_user_pool map[uint64]*User 13 | } 14 | 15 | func NewInfo(node_ *YNode.Info, uid uint64) YModule.Inter { 16 | _info := &Info{ 17 | M_user_pool: make(map[uint64]*User), 18 | } 19 | _info.Info = YModule.NewInfo(node_) 20 | return _info 21 | } 22 | func (i *Info) Init() { 23 | i.Info.Init(i) 24 | } 25 | 26 | func (i *Info) Close() { 27 | } 28 | 29 | func (i *Info) MSG_C2S_Login(s_ uint64, msg_ Msg.C2S_Login) { 30 | _, exists := i.M_user_pool[s_] 31 | if !exists { 32 | i.M_user_pool[s_] = NewUser(s_, s_) 33 | } 34 | i.Info.SendNetMsgJson(s_, Msg.S2C_Login{ 35 | //i.M_user_pool[s_].ToClientJson(nil), 36 | M_main_uid: s_, 37 | }) 38 | } 39 | 40 | func (i *Info) RPC_UserChangeCurrentMap(s_, map_uid_ uint64) { 41 | _user := i.M_user_pool[s_] 42 | if _user != nil { 43 | _user.M_current_map = map_uid_ 44 | } 45 | } 46 | 47 | func (i *Info) RPC_UserStartSwitchMap(user_uid_ uint64) { 48 | _user := i.M_user_pool[user_uid_] 49 | if _user != nil { 50 | _user.M_map_swtich_state = CONST_MAP_SWITCHING 51 | } 52 | } 53 | func (i *Info) RPC_UserFinishSwitchMap(user_uid_ uint64) { 54 | _user := i.M_user_pool[user_uid_] 55 | if _user != nil { 56 | _user.M_map_swtich_state = CONST_MAP_SWITCH_NONE 57 | } 58 | } 59 | 60 | func (i *Info) MSG_C2S_FirstEnterMap(s_ uint64, msg_ Msg.C2S_FirstEnterMap) { 61 | _user := i.M_user_pool[s_] 62 | if _user != nil { 63 | i.Info.RPCCall(YMsg.ToAgent("MapManager"), "FirstEnterMap", *_user) 64 | } 65 | } 66 | 67 | func (i *Info) MSG_C2S_UserMove(s_ uint64, msg_ Msg.C2S_UserMove) { 68 | _user := i.M_user_pool[s_] 69 | if _user == nil { 70 | return 71 | } 72 | 73 | i.Info.RPCCall(YMsg.ToAgent("Map", _user.M_current_map), "UserMove", _user.M_uid, msg_) 74 | } 75 | 76 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/ModuleRegister.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/yxinyi/YCServer/engine/BaseModule/NetModule" 5 | "github.com/yxinyi/YCServer/engine/YNode" 6 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Module/Map" 7 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Module/MapManager" 8 | "github.com/yxinyi/YCServer/examples/SeamlessExample/Server/Module/UserManager" 9 | _ "net/http/pprof" 10 | ) 11 | 12 | func ModuleCreateFuncLoad() { 13 | YNode.ModuleCreateFuncRegister("NewMap", Map.NewInfo) 14 | YNode.ModuleCreateFuncRegister("NetModule", NetModule.NewInfo) 15 | YNode.ModuleCreateFuncRegister("MapManager", MapManager.NewInfo) 16 | YNode.ModuleCreateFuncRegister("UserManager", UserManager.NewInfo) 17 | } 18 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/Util/Map.go: -------------------------------------------------------------------------------- 1 | package Util 2 | 3 | func GetTarSideNeighborMapIDList(side_ []bool, map_uid_ uint64) []uint64 { 4 | _neighbor_map_list := make([]uint64, 0) 5 | 6 | if side_[0] == true { 7 | _neighbor_map_list = append(_neighbor_map_list, map_uid_-(1<<32)) 8 | } 9 | if side_[1] == true { 10 | _neighbor_map_list = append(_neighbor_map_list, map_uid_+(1<<32)) 11 | } 12 | 13 | if side_[2] == true { 14 | _neighbor_map_list = append(_neighbor_map_list, map_uid_-1) 15 | } 16 | if side_[3] == true { 17 | _neighbor_map_list = append(_neighbor_map_list, map_uid_+1) 18 | } 19 | 20 | if side_[0] && side_[2] { 21 | _neighbor_map_list = append(_neighbor_map_list, map_uid_-1-(1<<32)) 22 | } 23 | 24 | if side_[0] && side_[3] { 25 | _neighbor_map_list = append(_neighbor_map_list, map_uid_+1-(1<<32)) 26 | } 27 | if side_[1] && side_[2] { 28 | _neighbor_map_list = append(_neighbor_map_list, map_uid_-1+(1<<32)) 29 | } 30 | if side_[1] && side_[3] { 31 | _neighbor_map_list = append(_neighbor_map_list, map_uid_+1+(1<<32)) 32 | } 33 | 34 | return _neighbor_map_list 35 | } 36 | 37 | func GetRoundNeighborMapIDList(map_uid_ uint64) []uint64 { 38 | return GetTarSideNeighborMapIDList([]bool{true, true, true, true}, map_uid_) 39 | } 40 | 41 | func MapOffDiff(cent_map_, offset_map_ uint64) (int, int) { 42 | _offset_up_down := int(offset_map_>>32)&0xFFFFFFFF 43 | _cent_up_down := int(cent_map_>>32)&0xFFFFFFFF 44 | _up_down_offset := _offset_up_down - _cent_up_down 45 | _left_right_offset := int(offset_map_&0xFFFFFFFF) - int(cent_map_&0xFFFFFFFF) 46 | return _up_down_offset, _left_right_offset 47 | } 48 | 49 | const ( 50 | CONST_MAP_OFFSET_ERROR = iota 51 | CONST_MAP_OFFSET_LEFT_UP 52 | CONST_MAP_OFFSET_UP 53 | CONST_MAP_OFFSET_RIGHT_UP 54 | CONST_MAP_OFFSET_LEFT 55 | CONST_MAP_OFFSET_RIGHT 56 | CONST_MAP_OFFSET_LEFT_DOWN 57 | CONST_MAP_OFFSET_DOWN 58 | CONST_MAP_OFFSET_RIGHT_DOWN 59 | ) 60 | 61 | func MapOffsetMask(main_map_uid_, offset_map_uid_ uint64) uint32 { 62 | _up_down_offset, _left_right_offset := MapOffDiff(main_map_uid_, offset_map_uid_) 63 | if _up_down_offset < 0 { 64 | if _left_right_offset < 0 { 65 | //左上角 66 | return CONST_MAP_OFFSET_LEFT_UP 67 | } 68 | if _left_right_offset == 0 { 69 | //正上方 70 | return CONST_MAP_OFFSET_UP 71 | } 72 | if _left_right_offset > 0 { 73 | //右上角 74 | return CONST_MAP_OFFSET_RIGHT_UP 75 | } 76 | } else if _up_down_offset > 0 { 77 | if _left_right_offset < 0 { 78 | //左下角 79 | return CONST_MAP_OFFSET_LEFT_DOWN 80 | } 81 | 82 | if _left_right_offset == 0 { 83 | //正下方 84 | return CONST_MAP_OFFSET_DOWN 85 | } 86 | 87 | if _left_right_offset > 0 { 88 | //右下角 89 | return CONST_MAP_OFFSET_RIGHT_DOWN 90 | } 91 | } else { 92 | if _left_right_offset < 0 { 93 | //正左角 94 | return CONST_MAP_OFFSET_LEFT 95 | } 96 | 97 | if _left_right_offset > 0 { 98 | //正右角 99 | return CONST_MAP_OFFSET_RIGHT 100 | } 101 | } 102 | return CONST_MAP_OFFSET_ERROR 103 | } 104 | 105 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/无缝地图切换方案.md: -------------------------------------------------------------------------------- 1 | ### 对象在无缝地图上切换 2 | 3 | ```sequence 4 | Client->UserManager:MSG_C2S_UserMove 5 | UserManager->Map:RPC_UserMove 6 | Map->Map:Moving:在边缘发生切换 7 | Map->UserManager:RPC_Map2UMStarSwitch 8 | UserManager->Map:RPC_UM2MapStarSwitch 9 | Client->UserManager: CacheAllMsg 10 | Map->NeighborMap:RPC_BetweenMapSwitch 11 | NeighborMap->UserManager:RPC_OverSwitch 12 | UserManager->NeighborMap:PushAllMsg 13 | Client->UserManager:MSG_C2S_UserMove 14 | UserManager->NeighborMap:RPC_UserMove 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/无缝地图动态创建方案.md: -------------------------------------------------------------------------------- 1 | ### 玩家登录新服务器 2 | 3 | ```sequence 4 | Client->UserManager:MSG_C2S_Login 5 | UserMgr->Client:MSG_C2S_Login 6 | Client->UserManager:C2S_FirstEnterMap 7 | UserMgr->MapManager:RPC_FirstEnterMap 8 | MapManager->YNode:RPC_NewModule:创建地图 9 | MapManager->Map:RPC_UserEnterMap 10 | MapManager->UserManager:RPC_UserChangeCurrentMap:用于后续路由 11 | Map->Client:S2C_FirstEnterMap 12 | ``` 13 | 14 | ### 单地图移动 15 | 16 | ```sequence 17 | Client->UserManager:MSG_C2S_UserMove 18 | UserManager->Map:RPC_UserMove:\n user上有当前MapId可以进行路由 19 | Map->Map: AStar寻路 20 | Map->Map: UserUpdate 21 | Map->Client:S2C_MapAStarNodeUpdate:定时同步 22 | Map->Client:S2C_MapAStarNodeUpdate:定时同步 23 | Map->Client:S2C_MapAStarNodeUpdate:定时同步 24 | ``` 25 | 26 | ### 移动至近边缘,创建未创建的邻近地图 27 | 28 | ```sequence 29 | Client->UserManager:MSG_C2S_UserMove 30 | UserManager->Map:RPC_UserMove:\n user上有当前MapId可以进行路由 31 | Map->Map:检测有玩家位于近边缘 32 | Map->Map:GetNeighborMap == nil 33 | Map->Map:CreateNeighborMapID 34 | Map->YNode:RPC_NewModule:创建地图 35 | Map->NeighborMap:RPC_CreateGhostEntity 36 | Client->Map: Msg_C2S_UserMove 37 | Map->Client:S2C_MapAStarNodeUpdate:定时同步 38 | Map->NeighborMap: RPC_DiffSyncEntity 39 | NeighborMap->NeighborUser:Msg_S2C_DiffSyncEntity 40 | Map->Client:S2C_MapAStarNodeUpdate:定时同步 41 | Map->NeighborMap: RPC_DiffSyncEntity 42 | NeighborMap->NeighborUser:Msg_S2C_DiffSyncEntity 43 | ``` 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /examples/SeamlessExample/Server/无缝地图边缘处理方案.md: -------------------------------------------------------------------------------- 1 | ### 边缘增加重叠区域 2 | 3 | ``` 4 | ┌───┬───────────────────┬───┐ 5 | │ │ │ │ 6 | ├───┼───────────────────┼───┤ 7 | │ │0 1 2 3 4 5 6 7 8 9│ │ 8 | │ │ │ │ 9 | │ │ │ │ 10 | │ │ │ │ 11 | │ │ │ │ 12 | │ │ │ │ 13 | │ │ │ │ 14 | ├───┼───────────────────┼───┤ 15 | │ │ │ │ 16 | └───┴───────────────────┴───┘ 17 | ``` 18 | 19 | 创建地图时四周留出一定的空白区域,动态创建地图时候,将地图的边缘信息拷贝到空白区域,如下 20 | 21 | ``` 22 | ┌───┬───────────────┬───┬───┬───────────────┬───┐ 23 | │ │ │ │ │ │ │ 24 | ├───┼───────────────┼───┼───┼───────────────┼───┤ 25 | │ │0 1 2 3 4 5 6 7│8 9│0 1│2 3 4 5 6 7 8 9│ │ 26 | │ │ │ │ │ │ │ 27 | │ │ │ │ │ │ │ 28 | │ │ 1 │ A │ B │ 2 │ │ 29 | │ │ │ │ │ │ │ 30 | │ │ │ │ │ │ │ 31 | │ │ │ │ │ │ │ 32 | ├───┼───────────────┼───┼───┼───────────────┼───┤ 33 | │ │ │ │ │ │ │ 34 | └───┴───────────────┴───┴───┴───────────────┴───┘ 35 | ``` 36 | 37 | 如果空白区域为5,则重叠区域为10,玩家在重叠区域可以根据需求灵活在两张地图间切换 38 | 39 | `寻路至邻居地图流程` 40 | 41 | ```sequence 42 | Client->UserManager:MSG_C2S_UserMove 43 | UserManager->Map:RPC_UserMove 44 | Map->Map:MovingUpdate 45 | Map->NeighborMap:SyncUser \n移动至邻近地图的重叠区域A就开始同步 46 | Map->NeighborMap:检测到移动至B重叠区域,就开始根据切换方案进行切换 47 | 48 | 49 | 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yxinyi/YCServer 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect 7 | github.com/hajimehoshi/ebiten/v2 v2.1.6 8 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect 9 | github.com/jonboulle/clockwork v0.2.2 // indirect 10 | github.com/json-iterator/go v1.1.12 11 | github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect 12 | github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f 13 | github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect 14 | github.com/spf13/pflag v1.0.5 15 | github.com/spf13/viper v1.9.0 16 | github.com/tebeka/strftime v0.1.5 // indirect 17 | github.com/yxinyi/YEventBus v0.1.2 18 | go.uber.org/zap v1.19.0 19 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d 20 | ) 21 | --------------------------------------------------------------------------------