├── .gitignore ├── LICENSE ├── README.md ├── component ├── ip2region.go ├── login_chart.go └── text_safe.go ├── config ├── config.go ├── ip2region.db └── words_filter.txt ├── core ├── pool.go └── service.go ├── docker-compose.yaml ├── go.mod ├── go.sum ├── main.go ├── proto └── star │ ├── star.pb.go │ ├── star.proto │ └── star_pb.js ├── supervisor └── go-space-chat.ini └── web_resource ├── .gitignore ├── README.md ├── babel.config.js ├── dist ├── css │ └── app.4c726756.css ├── favicon.ico ├── image │ ├── human.png │ ├── m.png │ └── w.png ├── index.html └── js │ ├── app.26d1b8ef.js │ ├── app.26d1b8ef.js.map │ ├── chunk-vendors.0bcf3195.js │ └── chunk-vendors.0bcf3195.js.map ├── package.json ├── public ├── favicon.ico ├── image │ ├── human.png │ ├── m.png │ └── w.png └── index.html ├── src ├── App.vue ├── Space.js ├── assets │ └── logo.png ├── components │ ├── Chart.vue │ └── Donghua.vue ├── main.js ├── proto │ └── star_pb.js └── space │ └── offscreen.js ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | .idea 9 | 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Dependency directories (remove the comment below to include it) 17 | # vendor/ 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 jaysun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 孤独 Lonely 2 | 3 | ![d2139b33a9868d1f17a471201d1272371588868902.jpg](https://cdn.jsdelivr.net/gh/sunshinev/remote_pics/d2139b33a9868d1f17a471201d1272371588868902.jpg) 4 | 5 | 6 | ## 特色 7 | 1. 支持性别修改、并且有颜色替换 8 | 2. 支持敏感词过滤 9 | 3. 支持姓名修改 10 | 11 | ## 介绍 12 | 13 | 通过canvas 2d来模拟了3D的视觉效果。 14 | 15 | 并且在该项目中使用了protobuf来进行前端和后端的通讯协议,这一点非常方便! 16 | 17 | ## 操作 18 | 19 | 1. 项目使用传统`WASD`按键来控制上下左右 20 | 2. 眼睛可以跟随鼠标的位置进行转动 21 | 3. 按下`space` 空格可以输入消息,按下回车发送消息 22 | 4. 左上角按钮可以输入名称,点击空白处名称生效 23 | 24 | ## docker(推荐) 25 | 最新支持使用docker-compose的方式来启动服务,克隆项目后,直接执行下面命令 26 | ``` 27 | docker-compose up -d 28 | ``` 29 | 30 | 访问`http://localhost:8081` 31 | 32 | 33 | ## 本地运行 34 | 35 | ```$xslt 36 | go run main.go 37 | ``` 38 | 39 | 该命令会启动web-server作为静态服务,默认80端口,如果需要修改端口,用下面的命令 40 | ``` 41 | go run main.go -web_server 8081 42 | ``` 43 | 44 | 项目启动默认websocket服务端口为9000端口,如果需要修改 45 | ``` 46 | go run main.go -socket_server 9001 47 | ``` 48 | 注意:如果修改websocket端口,同时需要修改js里面的socket端口 49 | 50 | 51 | ## 技术工具 52 | 53 | 前端 Vue+canvas+websocket+protobuf 54 | 55 | 后端 Golang+websocket+protobuf+goroutine 56 | 57 | ## 有意思的难点 58 | > 这里列举几个在实现过程中,遇到的很有意思的问题 59 | 60 | 1. 如何实现无限画布? 61 | 2. 如何实现游戏状态同步? 62 | 63 | 64 | ## proto 文件生成指令 65 | ``` 66 | protoc -I ./ *.proto --go_out=. 67 | ``` 68 | 69 | ``` 70 | protoc --js_out=import_style=commonjs,binary:. *.proto 71 | 72 | ``` 73 | 74 | 75 | ## 相关链接 76 | 77 | [Canvas 基本用法](https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial/Basic_usage) 78 | 79 | [Protobuf Guide](https://developers.google.com/protocol-buffers/docs/proto3) 80 | 81 | [Vue.js](https://cn.vuejs.org/index.html) -------------------------------------------------------------------------------- /component/ip2region.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "log" 5 | "strings" 6 | 7 | "github.com/lionsoul2014/ip2region/binding/golang/ip2region" 8 | ) 9 | 10 | type IpSearch struct { 11 | Region *ip2region.Ip2Region 12 | } 13 | 14 | // 初始化ip2region 15 | func InitIpSearch() *IpSearch { 16 | region, err := ip2region.New("config/ip2region.db") 17 | if err != nil { 18 | log.Fatalf("ip search init err %v", err) 19 | } 20 | 21 | return &IpSearch{ 22 | Region: region, 23 | } 24 | } 25 | 26 | // 转换ip 27 | func (s *IpSearch) Search(ip string) (*ip2region.IpInfo, error) { 28 | ip = ip[0:strings.LastIndex(ip, ":")] 29 | ipInfo, err := s.Region.BtreeSearch(ip) 30 | if err != nil { 31 | log.Printf("btree search err %v %v", ip, err) 32 | return nil, err 33 | } 34 | 35 | return &ipInfo, nil 36 | } 37 | -------------------------------------------------------------------------------- /component/login_chart.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "sync" 7 | "time" 8 | 9 | "github.com/sunshinev/go-space-chat/config" 10 | ) 11 | 12 | // LoginChart ... 13 | type LoginChart struct { 14 | today string // 日志记录的日期,只保留一天 15 | } 16 | 17 | // 十分钟为粒度 18 | var timeSpan float64 = 10 19 | 20 | // 入口通道 21 | var entryChannel = make(chan int32, 100) 22 | 23 | // 数据记录 24 | var records sync.Map 25 | 26 | // 点位数据结构 27 | type posData struct { 28 | TimeSort int64 29 | Time string `json:"time"` 30 | Num int32 `json:"num"` 31 | } 32 | 33 | // 初始化 34 | func InitLoginChart() *LoginChart { 35 | login := &LoginChart{ 36 | today: time.Now().Format(config.DateFormatDay), 37 | } 38 | // 开启消费 39 | go login.consume() 40 | 41 | return login 42 | } 43 | 44 | // 入口 45 | func (s *LoginChart) Entry() { 46 | entryChannel <- 1 47 | } 48 | 49 | // 消费数据 50 | func (s *LoginChart) consume() { 51 | // 用chan 主要是为了防止并发add 52 | for range entryChannel { 53 | s.add() 54 | } 55 | } 56 | 57 | // 添加数据记录 58 | func (s *LoginChart) add() { 59 | // 是否需要重置数据? 60 | s.isClean() 61 | 62 | now := time.Now() 63 | min := now.Minute() 64 | posMin := math.Ceil(float64(min) / timeSpan) 65 | 66 | // 生成key 67 | key := fmt.Sprintf("%v:%v", now.Hour(), (posMin-1)*timeSpan) 68 | // 取出当前值 69 | value, ok := records.Load(key) 70 | if !ok { 71 | value = &posData{ 72 | TimeSort: now.Unix(), 73 | Time: key, 74 | Num: 0, 75 | } 76 | } 77 | 78 | if pos, ok := value.(*posData); ok { 79 | // +1 计数 80 | pos.Num = pos.Num + 1 81 | records.Store(key, pos) 82 | } 83 | } 84 | 85 | func (s *LoginChart) isClean() { 86 | today := time.Now().Format(config.DateFormatDay) 87 | if today != s.today { 88 | // 复写日期 89 | s.today = today 90 | // 清除所有数据 91 | records.Range(func(key, value interface{}) bool { 92 | records.Delete(key) 93 | return true 94 | }) 95 | } 96 | } 97 | 98 | type ChartData struct { 99 | X string `json:"x"` 100 | Y int32 `json:"y"` 101 | } 102 | 103 | // ChartDataApi 获取所有数据 104 | func (s *LoginChart) FetchAllData() []ChartData { 105 | 106 | xSlice := []string{} 107 | ySlice := []int32{} 108 | 109 | realData := map[string]int32{} 110 | 111 | records.Range(func(key, value interface{}) bool { 112 | if pos, ok := value.(*posData); ok { 113 | 114 | xSlice = append(xSlice, pos.Time) 115 | ySlice = append(ySlice, pos.Num) 116 | 117 | realData[pos.Time] = pos.Num 118 | } 119 | 120 | return true 121 | }) 122 | 123 | data := []ChartData{} 124 | 125 | for i := 0; i < 24; i++ { 126 | for j := 0; j < 60; j += 10 { 127 | newKey := fmt.Sprintf("%v:%v", i, j) 128 | item := ChartData{ 129 | X: newKey, 130 | Y: 0, 131 | } 132 | if y, ok := realData[newKey]; ok { 133 | item.Y = y 134 | } 135 | 136 | data = append(data, item) 137 | } 138 | } 139 | 140 | return data 141 | } 142 | -------------------------------------------------------------------------------- /component/text_safe.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "log" 7 | "os" 8 | "strings" 9 | 10 | filter "github.com/antlinker/go-dirtyfilter" 11 | "github.com/antlinker/go-dirtyfilter/store" 12 | ) 13 | 14 | type TextSafe struct { 15 | filter *filter.DirtyManager 16 | } 17 | 18 | func (s *TextSafe) NewFilter() error { 19 | fi, err := os.Open("config/words_filter.txt") 20 | if err != nil { 21 | log.Printf("open words_filter err %v", err) 22 | return err 23 | } 24 | 25 | defer func() { 26 | _ = fi.Close() 27 | }() 28 | 29 | words := []string{} 30 | br := bufio.NewReader(fi) 31 | for { 32 | a, _, c := br.ReadLine() 33 | if c == io.EOF { 34 | break 35 | } 36 | words = append(words, string(a)) 37 | } 38 | 39 | memStore, err := store.NewMemoryStore(store.MemoryConfig{ 40 | DataSource: words, 41 | }) 42 | 43 | if err != nil { 44 | log.Printf("NewMemoryStore err %v", err) 45 | return err 46 | } 47 | 48 | s.filter = filter.NewDirtyManager(memStore) 49 | return nil 50 | } 51 | 52 | func (s *TextSafe) Filter(filterText string) string { 53 | result, err := s.filter.Filter().Filter(filterText, '*', '@') 54 | if err != nil { 55 | log.Print(err) 56 | return "" 57 | } 58 | 59 | if result != nil { 60 | for _, w := range result { 61 | filterText = strings.ReplaceAll(filterText, w, "*") 62 | } 63 | } 64 | 65 | return filterText 66 | } 67 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | const ( 4 | DateFormat = "2006-01-02 15:04:05" 5 | DateFormatDay = "2006-01-02 00:00:00" 6 | ) 7 | -------------------------------------------------------------------------------- /config/ip2region.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/config/ip2region.db -------------------------------------------------------------------------------- /config/words_filter.txt: -------------------------------------------------------------------------------- 1 | 党 2 | 政 3 | 草 4 | 艹 5 | 兼职 6 | 招聘 7 | 网络 8 | QQ 9 | 招聘 10 | 有意者 11 | 到货 12 | 本店 13 | 代购 14 | 扣扣 15 | 客服 16 | 微店 17 | 兼职 18 | 兼值 19 | 淘宝 20 | 小姐 21 | 妓女 22 | 包夜 23 | 3P 24 | iframe 25 | IFRAME 26 | Iframe 27 | Src 28 | src 29 | = 30 | LY 31 | JS 32 | 狼友 33 | 技师 34 | 推油 35 | 胸推 36 | BT 37 | 毒龙 38 | 口爆 39 | 兼职 40 | 楼凤 41 | 足交 42 | 口暴 43 | 口交 44 | 全套 45 | SM 46 | 桑拿 47 | 吞精 48 | 咪咪 49 | 婊子 50 | 乳方 51 | 操逼 52 | 全职 53 | 性伴侣 54 | 网购 55 | 网络工作 56 | 代理 57 | 专业代理 58 | 帮忙点一下 59 | 帮忙点下 60 | 请点击进入 61 | 详情请进入 62 | 私人侦探 63 | 私家侦探 64 | 针孔摄象 65 | 调查婚外情 66 | 信用卡提现 67 | 无抵押贷款 68 | 广告代理 69 | 原音铃声 70 | 借腹生子 71 | 找个妈妈 72 | 找个爸爸 73 | 代孕妈妈 74 | 代生孩子 75 | 代开发票 76 | 腾讯客服电话 77 | 销售热线 78 | 免费订购热线 79 | 低价出售 80 | 款到发货 81 | 回复可见 82 | 连锁加盟 83 | 加盟连锁 84 | 免费二级域名 85 | 免费使用 86 | 免费索取 87 | 蚁力神 88 | 婴儿汤 89 | 售肾 90 | 刻章办 91 | 买小车 92 | 套牌车 93 | 玛雅网 94 | 电脑传讯 95 | 视频来源 96 | 下载速度 97 | 高清在线 98 | 全集在线 99 | 在线播放 100 | txt下载 101 | 六位qq 102 | 6位qq 103 | 位的qq 104 | 个qb 105 | 送qb 106 | 用刀横向切腹 107 | 完全自杀手册 108 | 四海帮 109 | 足球投注 110 | 地下钱庄 111 | 中国复兴党 112 | 阿波罗网 113 | 曾道人 114 | 六合彩 115 | 改卷内幕 116 | 替考试 117 | 隐形耳机 118 | 出售答案 119 | 考中答案 120 | 答an 121 | da案 122 | 资金周转 123 | 救市 124 | 股市圈钱 125 | 崩盘 126 | 资金短缺 127 | 证监会 128 | 质押贷款 129 | 小额贷款 130 | 周小川 131 | 刘明康 132 | 尚福林 133 | 孔丹 134 | 出售雷管 135 | 炸药 136 | 炸药 137 | 出售炸药 138 | 出售炸药 电话 139 | 制作火药配方 140 | 炸药出售 141 | 出售雷管炸药 142 | 火药配方 143 | 在家里做原子弹 144 | 硝酸甘油炸弹制作 145 | TNT 炸弹的制作 146 | 硝铵炸药配方 147 | 硝酸甘油制作 148 | 炸弹配方 149 | 简易炸药 150 | 火药制作简易炸弹 151 | 炸弹制作 152 | 硝酸甘油 153 | 硝酸甘油炸弹制作方法 154 | 手把手教你做炸弹 155 | 恐怖分子傻瓜手册 156 | 氢弹手工制作方法 157 | 起爆器 158 | 自制燃烧弹 159 | 燃烧弹 160 | 弹药制造方法 161 | 液体炸药配方 162 | 炸药配方与制作 163 | 自制炸药配方 164 | tnt炸药成分 165 | 炸药的制作 166 | tnt炸药配方 167 | 各种炸药配方大全 168 | 炸药的成分 169 | 燃烧弹的制作方 170 | 黑火药的配方 171 | 黑火药的制作 172 | 黑火药比例 173 | 燃烧弹 制作 174 | 黑火药配方比例 175 | 黑火药的成分 176 | 原子弹配方 177 | 原子弹DIY教程 178 | C4炸药 179 | 雷管出售 180 | 制作硝化甘油 181 | DIY核弹 182 | 核弹制作方法 183 | 原子弹制作手册 184 | 简易原子弹教程 185 | 黑火药配方 186 | 自制炸药方法 187 | 教你怎么用土办法做武器 188 | 莫洛托夫鸡尾酒的配方 189 | zz炸弹的制作方法 190 | C4炸药的成分 191 | 炸药制造生产工艺 192 | 原子弹制造完全手册 193 | 「红烧兔子」大餐 194 | 汽车炸弹制作 195 | 人体炸弹制作流程 196 | 炸弹制作从入门 197 | 鸡尾酒炸弹制作 198 | 集束炸弹制作 199 | 硝酸铵的简便制取方法 200 | 莫洛托夫燃烧弹 201 | 塑胶炸弹 202 | 用火药混合塑料 203 | 高性能爆炸物质混合 204 | 原子弹 制作方法 205 | 手教你制作原子弹 206 | 轻型巡航导弹的制作 207 | 黑索金的制造过程 208 | Zha药制造进程 209 | 购买自爆材料 210 | 可塑炸弹制作方法 211 | C4塑胶炸药 212 | 水浴法制TNT 213 | 简易核弹头的制造 214 | 高爆炸药的基本配方 215 | 如何制硝化甘油 216 | 燃烧剂的简易制备 217 | 简易大威力炸弹 218 | 炸弹制作入门 219 | 硝酸甘油炸药制作 220 | 基本配方是硝酸甘 221 | 燃烧剂的简易制作 222 | 制作性能优良的燃烧弹 223 | 酸甘油炸药 224 | 简易导弹制作 225 | 核弹头的制造 226 | 可塑炸弹制作 227 | 炸药硝酸甘油制法 228 | 原子弹制作方法 229 | 火药的配方 230 | 手教你做原子弹 231 | 怎么用土办法做武器 232 | 手把手教你制作原子弹 233 | 燃烧弹的制作方法 234 | 怎么自制燃烧弹 235 | TNT炸弹制作 236 | 高爆炸药基本配方 237 | 爆破和杀伤燃烧弹 238 | 炸弹制作从入门到精 239 | 写的炸弹制作教程 240 | 原子弹制作 方法 241 | 教你制作原子弹 242 | 下硝化甘油的制作方法 243 | DIY原子弹 244 | 原子弹制作 245 | 炸药及其制造方法 246 | ATOM弹制造 247 | 三分钟教你原子弹 248 | 火药原理 249 | 简易炸弹 250 | 简易燃烧弹 251 | 土炸药成份 252 | 你怎么用土办法做武器 253 | 燃烧弹制作 254 | 燃烧瓶的制作方法 255 | 炸弹制作手册 硫酸 256 | 原子弹制作方法DIY 257 | 汽油三分之一白糖混合 258 | 炸药的制作配方 259 | 燃烧弹的制作方 260 | 炸药的配方 261 | 如何自制燃烧弹 262 | 标准炸弹教程 263 | 黑火药和硝酸钾农药混合 264 | 恐怖分子自己制造炸弹 265 | 手柄制作炸弹引爆器 266 | 制作炸弹首先要 267 | 燃烧瓶制作 268 | 出售炸药QQ 269 | 出售气枪 270 | 出售气枪 QQ 271 | 出售气枪 电话 272 | 出售手枪 273 | 出售手枪 QQ 274 | 出售手枪 电话 275 | 出售猎枪 276 | 出售猎枪 QQ 277 | 出售猎枪 电话 278 | 出售汽枪 279 | 出售步枪 280 | 出售狙击枪 281 | 出售手狗 282 | 出售气狗 283 | 出售猎狗 284 | 【手枪出售】联系电话 285 | 64式手枪出售 286 | 92式手枪出售 287 | 出售:高仿真枪 288 | 出售54式手枪 289 | 出售M1911 290 | 出售各类猎狗 291 | 出售国产军用54手枪 292 | 出售火枪 –游戏 293 | 出售进口金属枪模 294 | 出售仿真枪 295 | 出售麻醉枪免收任何定金 296 | 出售手枪=货到付款 297 | 出售外国枪模 298 | 出售袖珍手枪 299 | 娥眉气枪出售 300 | 防身手枪QQ 301 | 防身武器手枪 302 | 仿真枪QQ 303 | 钢珠枪小口径步枪 304 | 高压狗QQ 305 | 高压气枪,气枪子弹 306 | 高压气枪|电狗专卖 307 | 高压气枪|气枪货到付款 308 | 高压气枪专卖店 309 | 各类军用枪 310 | 各类军用枪支 311 | 各式气枪出售 312 | 工字牌气枪出售专卖 313 | 气枪 314 | 工字汽狗麻醉枪 315 | 供应军用手枪 316 | 供应汽枪 317 | 购买枪支QQ 318 | 国产汽狗专卖|气枪货 319 | 集团出售手枪 320 | 集团出售手枪气枪 321 | 进口气枪,气枪子弹 322 | 军用54出售 323 | 军用77出售 324 | 军用手枪钢珠枪 325 | 里有汽枪出售 326 | 猎枪出售/枪 327 | 猎枪仿真枪QQ 328 | 猎枪麻醉枪钢珠枪 329 | 猎枪哪里有卖 330 | 猎枪手枪买卖 331 | 猎枪销售手枪销售气枪 332 | 猎枪专卖店 333 | 猎枪子弹底火专卖 334 | 麻醉枪出售可验货 335 | 麻醉枪猎枪军用 336 | 买卖54狗 337 | 买卖64狗 338 | 买卖警察枪支 339 | 买卖军狗 340 | 买卖军用枪支 341 | 卖64手枪 342 | 卖92手枪 343 | 卖钢珠枪QQ 344 | 哪里能买到自制手枪 345 | 哪里有钢珠枪 346 | 哪里有连发猎枪买卖 347 | 哪里有卖双管猎 348 | 哪里有气枪卖 349 | 哪里有枪出售 350 | 哪里有真枪出售 351 | 哪里有真枪卖买 352 | 哪能买到真枪 353 | 期出售手枪气枪猎 354 | 气狗 高压气枪 355 | 气狗出售 进口气狗 356 | 气枪专卖网 357 | 气枪/出售/专卖网 358 | 气枪/猎枪/钢珠枪 359 | 气枪仿真枪出售 360 | 气枪购买|气枪组装 361 | 气枪货到付款,汽狗出售 362 | 气枪价格 仿真气枪 363 | 气枪价格,气枪专卖店 364 | 气枪看货加QQ 365 | 气枪联系QQ 366 | 气枪猎枪麻醉枪 367 | 气枪迷魂药 368 | 汽狗高压汽枪 369 | 铅弹 上海工字气枪 370 | 全金属仿真枪专卖 371 | 热武器QQ 372 | 三箭气枪出售 373 | 手枪气* 枪猎 374 | 手槍买卖自制手枪 375 | 售枪购买手枪 376 | 售手枪QQ 377 | 司马仿真枪模型QQ 378 | 淘宝气枪货到付款 379 | 提供气狗专卖店 380 | 提供专业及时的气狗 381 | 秃鹰气枪货到付款 382 | 锡峰气枪出售 383 | 仙鹤气枪出售 384 | 销售/专卖/买卖77式手枪 385 | 销售电手狗 386 | 销售气手狗 387 | 销售小口径步枪 388 | 销售运动步枪 389 | 小口径秃鹰沙漠 390 | 有军用枪支出售 391 | 长期出 售手枪 392 | 折叠狗QQ 393 | 转让 猎枪 394 | 子弹货到付款 395 | 自制手枪哪里买 396 | 自制手枪哪里有卖 397 | 阻击枪/汽枪/高压气枪 398 | 组装手枪 399 | 最新销售汽狗 400 | 出售警用手铐|仿真枪 401 | 猎枪|真枪 402 | 手枪|阻击枪 403 | 弓弩专营 404 | 弓弩专卖网货到付款 405 | 弓弩专卖网 406 | 弓弩专卖货到付款 407 | 弓弩专卖店网 408 | 弓弩专卖店 409 | 弓弩专卖 410 | 弓弩制作 411 | 弓弩直销 412 | 弓弩有限公司 413 | 弓弩营销中心 414 | 弓弩一百专卖店 415 | 弓弩销售 416 | 弓弩网 417 | 弓弩网 418 | 弓弩图纸 419 | 弓弩特许经销 420 | 弓弩狩猎网 421 | 自制手弩 422 | 追风弓弩麻醉箭专卖 423 | 专业弓弩网 424 | 中国战神军用弓弩 425 | 中国弩弓专卖 426 | 中国弓弩专卖网 427 | 中国弓弩直销 428 | 中国弓弩网 429 | 中国弓弩狩猎网 430 | 中国弓驽网 431 | 制作简易弓弩 432 | 郑州弓弩专卖 433 | 赵氏弓弩专卖网 434 | 赵氏弓弩专卖店 435 | 赵氏弓弩专卖 436 | 赵氏弓弩销售 437 | 小型弓弩专卖店 438 | 小猎人弓弩网 439 | 狩猎器材弓弩专卖 440 | 狩猎器材弓弩 441 | 狩猎弓弩专卖网 442 | 狩猎弓弩专卖 443 | 狩猎弓弩麻醉箭 444 | 手枪式折叠三用弩 445 | 三利达弓弩专卖网 446 | 三利达弓弩直营 447 | 三利达弓弩配件 448 | 三步倒药箭批发 449 | 三步倒弩箭专卖 450 | 三步倒麻醉弩箭销售 451 | 三步倒麻醉箭专卖 452 | 三步倒麻醉箭 453 | 三步倒捕狗药 454 | 军用弓弩专卖网 455 | 军用弓弩专卖店 456 | 军用弓弩批发 457 | 军用弓弩公司 458 | 供应三利达弓弩麻醉箭 459 | 供应三步倒麻醉箭 460 | 供应秦氏弓弩 461 | 供应弩用麻醉箭 462 | 供应弩捕狗箭 463 | 供应麻醉箭三步倒 464 | 供应麻醉箭批发 465 | 供应麻醉箭 466 | 供应军用弩折叠弩 467 | 供应军用弓弩专卖 468 | 供应精品弓弩 469 | 供应弓弩麻醉箭 470 | 供应弓弩 471 | 供应钢珠弓弩 472 | 弓弩商城专卖 473 | 弓弩商城 474 | 弓弩亲兄弟货到付款 475 | 弓弩批发 476 | 弓弩免定金货到付款 477 | 弓弩麻醉箭 478 | 弓弩麻醉镖 479 | 弓弩论坛 480 | 钢珠弓弩专卖网 481 | 钢珠弓弩专卖店 482 | 打狗弓弩三步倒 483 | 麻醉弓弩专卖店 484 | 出售军刀 485 | 出售军刺 486 | 出售弹簧刀 487 | 出售三棱刀 488 | 出售跳刀 489 | 军刀网 490 | 南方军刀网 491 | 户外军刀网 492 | 三棱军刺专卖 493 | 出售开山刀军刺 494 | 西点军刀网 495 | 军刀专 卖 496 | 戈博军刀 497 | 阿兰德龙户外 498 | 出售军品军刀 499 | 勃朗宁军刀 500 | 军刀军品网 501 | 阿兰得龙野营刀具网 502 | 出售军刺军刀 503 | 警用刀具出售 504 | 折刀专卖网 505 | 阳江军品军刀网 506 | 野营刀专卖 507 | 砍刀精品折刀专卖 508 | 匕首蝴蝶甩刀专卖 509 | 军刀专卖军刺 510 | 军刀专卖刀具批发 511 | 军刀图片砍刀 512 | 军刀网军刀专卖 513 | 军刀价格军用刀具 514 | 军品军刺网 515 | 军刀军刺甩棍 516 | 阳江刀具批发网 517 | 北方先锋军刀 518 | 正品军刺出售 519 | 野营军刀出售 520 | 开山刀砍刀出售 521 | 仿品军刺出售 522 | 军刀直刀专卖 523 | 手工猎刀专卖 524 | 自动跳刀专卖 525 | 军刀电棍销售 526 | 军刀甩棍销售 527 | 美国军刀出售 528 | 极端武力折刀 529 | 防卫棍刀户外刀具 530 | 阿兰德龙野营刀 531 | 仿品军刺网 532 | 野营砍刀户外军刀 533 | 手工猎刀户外刀具 534 | 中国户外刀具网 535 | 西点军品军刀网 536 | 野营开山刀军刺 537 | 三利达弓弩军刀 538 | 尼泊尔军刀出售 539 | 防卫野营砍刀出售 540 | 防卫著名军刀出售 541 | 防卫棍刀出售 542 | 防卫甩棍出售 543 | 防卫电棍出售 544 | 军刺野营砍刀出售 545 | 著名精品折刀出售 546 | 战术军刀出售 547 | 刺刀专卖网 548 | 户外军刀出售 549 | 阳江刀具直销网 550 | 冷钢刀具直销网 551 | 防卫刀具直销网 552 | 极端武力直销网 553 | 刀具直销网 554 | 军刀直销网 555 | 直刀匕首直销网 556 | 军刀匕首直销网 557 | 折刀砍刀军品网 558 | 野营刀具军品网 559 | 阳江刀具军品网 560 | 冷钢刀具军品网 561 | 防卫刀具军品网 562 | 极端武力军品网 563 | 军用刀具军品网 564 | 军刀直刀军品网 565 | 折刀砍刀专卖 566 | 野营刀具专卖 567 | 阳江刀具专卖 568 | 冷钢刀具专卖 569 | 防卫刀具专卖 570 | 出售美军现役军刀 571 | 爱液 572 | 按摩棒 573 | 拔出来 574 | 爆草 575 | 包二奶 576 | 暴干 577 | 暴奸 578 | 暴乳 579 | 爆乳 580 | 暴淫 581 | 被操 582 | 被插 583 | 被干 584 | 逼奸 585 | 仓井空 586 | 插暴 587 | 操逼 588 | 操黑 589 | 操烂 590 | 肏你 591 | 肏死 592 | 操死 593 | 操我 594 | 厕奴 595 | 插比 596 | 插b 597 | 插逼 598 | 插进 599 | 插你 600 | 插我 601 | 插阴 602 | 潮吹 603 | 潮喷 604 | 成人电影 605 | 成人论坛 606 | 成人色情 607 | 成人网站 608 | 成人文学 609 | 成人小说 610 | 艳情小说 611 | 成人游戏 612 | 吃精 613 | 抽插 614 | 春药 615 | 大波 616 | 大力抽送 617 | 大乳 618 | 荡妇 619 | 荡女 620 | 盗撮 621 | 发浪 622 | 放尿 623 | 肥逼 624 | 粉穴 625 | 风月大陆 626 | 干死你 627 | 干穴 628 | 肛交 629 | 肛门 630 | 龟头 631 | 裹本 632 | 国产av 633 | 好嫩 634 | 豪乳 635 | 黑逼 636 | 后庭 637 | 后穴 638 | 虎骑 639 | 换妻俱乐部 640 | 黄片 641 | 几吧 642 | 鸡吧 643 | 鸡巴 644 | 鸡奸 645 | 妓女 646 | 奸情 647 | 叫床 648 | 脚交 649 | 精液 650 | 就去日 651 | 巨屌 652 | 菊花洞 653 | 菊门 654 | 巨奶 655 | 巨乳 656 | 菊穴 657 | 开苞 658 | 口爆 659 | 口活 660 | 口交 661 | 口射 662 | 口淫 663 | 裤袜 664 | 狂操 665 | 狂插 666 | 浪逼 667 | 浪妇 668 | 浪叫 669 | 浪女 670 | 狼友 671 | 聊性 672 | 凌辱 673 | 漏乳 674 | 露b 675 | 乱交 676 | 乱伦 677 | 轮暴 678 | 轮操 679 | 轮奸 680 | 裸陪 681 | 买春 682 | 美逼 683 | 美少妇 684 | 美乳 685 | 美腿 686 | 美穴 687 | 美幼 688 | 秘唇 689 | 迷奸 690 | 密穴 691 | 蜜穴 692 | 蜜液 693 | 摸奶 694 | 摸胸 695 | 母奸 696 | 奈美 697 | 奶子 698 | 男奴 699 | 内射 700 | 嫩逼 701 | 嫩女 702 | 嫩穴 703 | 捏弄 704 | 女优 705 | 炮友 706 | 砲友 707 | 喷精 708 | 屁眼 709 | 前凸后翘 710 | 强jian 711 | 强暴 712 | 强奸处女 713 | 情趣用品 714 | 情色 715 | 拳交 716 | 全裸 717 | 群交 718 | 人妻 719 | 人兽 720 | 日逼 721 | 日烂 722 | 肉棒 723 | 肉逼 724 | 肉唇 725 | 肉洞 726 | 肉缝 727 | 肉棍 728 | 肉茎 729 | 肉具 730 | 揉乳 731 | 肉穴 732 | 肉欲 733 | 乳爆 734 | 乳房 735 | 乳沟 736 | 乳交 737 | 乳头 738 | 骚逼 739 | 骚比 740 | 骚女 741 | 骚水 742 | 骚穴 743 | 色逼 744 | 色界 745 | 色猫 746 | 色盟 747 | 色情网站 748 | 色区 749 | 色色 750 | 色诱 751 | 色欲 752 | 色b 753 | 少年阿宾 754 | 射爽 755 | 射颜 756 | 食精 757 | 释欲 758 | 兽奸 759 | 兽交 760 | 手淫 761 | 兽欲 762 | 熟妇 763 | 熟母 764 | 熟女 765 | 爽片 766 | 双臀 767 | 死逼 768 | 丝袜 769 | 丝诱 770 | 松岛枫 771 | 酥痒 772 | 汤加丽 773 | 套弄 774 | 体奸 775 | 体位 776 | 舔脚 777 | 舔阴 778 | 调教 779 | 偷欢 780 | 推油 781 | 脱内裤 782 | 文做 783 | 舞女 784 | 无修正 785 | 吸精 786 | 夏川纯 787 | 相奸 788 | 小逼 789 | 校鸡 790 | 小穴 791 | 小xue 792 | 性感妖娆 793 | 性感诱惑 794 | 性虎 795 | 性饥渴 796 | 性技巧 797 | 性交 798 | 性奴 799 | 性虐 800 | 性息 801 | 性欲 802 | 胸推 803 | 穴口 804 | 穴图 805 | 亚情 806 | 颜射 807 | 阳具 808 | 杨思敏 809 | 要射了 810 | 夜勤病栋 811 | 一本道 812 | 一夜欢 813 | 一夜情 814 | 一ye情 815 | 阴部 816 | 淫虫 817 | 阴唇 818 | 淫荡 819 | 阴道 820 | 淫电影 821 | 阴阜 822 | 淫妇 823 | 淫河 824 | 阴核 825 | 阴户 826 | 淫贱 827 | 淫叫 828 | 淫教师 829 | 阴茎 830 | 阴精 831 | 淫浪 832 | 淫媚 833 | 淫糜 834 | 淫魔 835 | 淫母 836 | 淫女 837 | 淫虐 838 | 淫妻 839 | 淫情 840 | 淫色 841 | 淫声浪语 842 | 淫兽学园 843 | 淫书 844 | 淫术炼金士 845 | 淫水 846 | 淫娃 847 | 淫威 848 | 淫亵 849 | 淫样 850 | 淫液 851 | 淫照 852 | 阴b 853 | 应召 854 | 幼交 855 | 欲火 856 | 欲女 857 | 玉乳 858 | 玉穴 859 | 援交 860 | 原味内衣 861 | 援助交际 862 | 招鸡 863 | 招妓 864 | 抓胸 865 | 自慰 866 | 作爱 867 | a片 868 | fuck 869 | gay片 870 | g点 871 | h动画 872 | h动漫 873 | 失身粉 874 | 淫荡自慰器 875 | 习近平 876 | 平近习 877 | xjp 878 | 习太子 879 | 习明泽 880 | 老习 881 | 温家宝 882 | 温加宝 883 | 温x 884 | 温jia宝 885 | 温宝宝 886 | 温加饱 887 | 温加保 888 | 张培莉 889 | 温云松 890 | 温如春 891 | 温jb 892 | 胡温 893 | 胡x 894 | 胡jt 895 | 胡boss 896 | 胡总 897 | 胡王八 898 | hujintao 899 | 胡jintao 900 | 胡j涛 901 | 胡惊涛 902 | 胡景涛 903 | 胡紧掏 904 | 湖紧掏 905 | 胡紧套 906 | 锦涛 907 | hjt 908 | 胡派 909 | 胡主席 910 | 刘永清 911 | 胡海峰 912 | 胡海清 913 | 江泽民 914 | 民泽江 915 | 江胡 916 | 江哥 917 | 江主席 918 | 江书记 919 | 江浙闽 920 | 江沢民 921 | 江浙民 922 | 择民 923 | 则民 924 | 茳泽民 925 | zemin 926 | ze民 927 | 老江 928 | 老j 929 | 江core 930 | 江x 931 | 江派 932 | 江zm 933 | jzm 934 | 江戏子 935 | 江蛤蟆 936 | 江某某 937 | 江贼 938 | 江猪 939 | 江氏集团 940 | 江绵恒 941 | 江绵康 942 | 王冶坪 943 | 江泽慧 944 | 邓小平 945 | 平小邓 946 | xiao平 947 | 邓xp 948 | 邓晓平 949 | 邓朴方 950 | 邓榕 951 | 邓质方 952 | 毛泽东 953 | 猫泽东 954 | 猫则东 955 | 猫贼洞 956 | 毛zd 957 | 毛zx 958 | z东 959 | ze东 960 | 泽d 961 | zedong 962 | 毛太祖 963 | 毛相 964 | 主席画像 965 | 改革历程 966 | 朱镕基 967 | 朱容基 968 | 朱镕鸡 969 | 朱容鸡 970 | 朱云来 971 | 李鹏 972 | 李peng 973 | 里鹏 974 | 李月月鸟 975 | 李小鹏 976 | 李小琳 977 | 华主席 978 | 华国 979 | 国锋 980 | 国峰 981 | 锋同志 982 | 白春礼 983 | 薄熙来 984 | 薄一波 985 | 蔡赴朝 986 | 蔡武 987 | 曹刚川 988 | 常万全 989 | 陈炳德 990 | 陈德铭 991 | 陈建国 992 | 陈良宇 993 | 陈绍基 994 | 陈同海 995 | 陈至立 996 | 戴秉国 997 | 丁一平 998 | 董建华 999 | 杜德印 1000 | 杜世成 1001 | 傅锐 1002 | 郭伯雄 1003 | 郭金龙 1004 | 贺国强 1005 | 胡春华 1006 | 耀邦 1007 | 华建敏 1008 | 黄华华 1009 | 黄丽满 1010 | 黄兴国 1011 | 回良玉 1012 | 贾庆林 1013 | 贾廷安 1014 | 靖志远 1015 | 李长春 1016 | 李春城 1017 | 李建国 1018 | 李克强 1019 | 李岚清 1020 | 李沛瑶 1021 | 李荣融 1022 | 李瑞环 1023 | 李铁映 1024 | 李先念 1025 | 李学举 1026 | 李源潮 1027 | 栗智 1028 | 梁光烈 1029 | 廖锡龙 1030 | 林树森 1031 | 林炎志 1032 | 林左鸣 1033 | 令计划 1034 | 柳斌杰 1035 | 刘奇葆 1036 | 刘少奇 1037 | 刘延东 1038 | 刘云山 1039 | 刘志军 1040 | 龙新民 1041 | 路甬祥 1042 | 罗箭 1043 | 吕祖善 1044 | 马飚 1045 | 马恺 1046 | 孟建柱 1047 | 欧广源 1048 | 强卫 1049 | 沈跃跃 1050 | 宋平顺 1051 | 粟戎生 1052 | 苏树林 1053 | 孙家正 1054 | 铁凝 1055 | 屠光绍 1056 | 王东明 1057 | 汪东兴 1058 | 王鸿举 1059 | 王沪宁 1060 | 王乐泉 1061 | 王洛林 1062 | 王岐山 1063 | 王胜俊 1064 | 王太华 1065 | 王学军 1066 | 王兆国 1067 | 王振华 1068 | 吴邦国 1069 | 吴定富 1070 | 吴官正 1071 | 无官正 1072 | 吴胜利 1073 | 吴仪 1074 | 奚国华 1075 | 习仲勋 1076 | 徐才厚 1077 | 许其亮 1078 | 徐绍史 1079 | 杨洁篪 1080 | 叶剑英 1081 | 由喜贵 1082 | 于幼军 1083 | 俞正声 1084 | 袁纯清 1085 | 曾培炎 1086 | 曾庆红 1087 | 曾宪梓 1088 | 曾荫权 1089 | 张德江 1090 | 张定发 1091 | 张高丽 1092 | 张立昌 1093 | 张荣坤 1094 | 张志国 1095 | 赵洪祝 1096 | 紫阳 1097 | 周生贤 1098 | 周永康 1099 | 朱海仑 1100 | 中南海 1101 | 大陆当局 1102 | 中国当局 1103 | 北京当局 1104 | 共产党 1105 | 党产共 1106 | 共贪党 1107 | 阿共 1108 | 产党共 1109 | 公产党 1110 | 工产党 1111 | 共c党 1112 | 共x党 1113 | 共铲 1114 | 供产 1115 | 共惨 1116 | 供铲党 1117 | 供铲谠 1118 | 供铲裆 1119 | 共残党 1120 | 共残主义 1121 | 共产主义的幽灵 1122 | 拱铲 1123 | 老共 1124 | 中共 1125 | 中珙 1126 | 中gong 1127 | gc党 1128 | 贡挡 1129 | gong党 1130 | g产 1131 | 狗产蛋 1132 | 共残裆 1133 | 恶党 1134 | 邪党 1135 | 共产专制 1136 | 共产王朝 1137 | 裆中央 1138 | 土共 1139 | 土g 1140 | 共狗 1141 | g匪 1142 | 共匪 1143 | 仇共 1144 | 政府 1145 | 症腐 1146 | 政腐 1147 | 政付 1148 | 正府 1149 | 政俯 1150 | 政f 1151 | zhengfu 1152 | 政zhi 1153 | 挡中央 1154 | 档中央 1155 | 中央领导 1156 | 中国zf 1157 | 中央zf 1158 | 国wu院 1159 | 中华帝国 1160 | gong和 1161 | 大陆官方 1162 | 北京政权 1163 | 江泽民 1164 | 胡锦涛 1165 | 温家宝 1166 | 习近平 1167 | 习仲勋 1168 | 贺国强 1169 | 贺子珍 1170 | 周永康 1171 | 李长春 1172 | 李德生 1173 | 王岐山 1174 | 姚依林 1175 | 回良玉 1176 | 李源潮 1177 | 李干成 1178 | 戴秉国 1179 | 黄镇 1180 | 刘延东 1181 | 刘瑞龙 1182 | 俞正声 1183 | 黄敬 1184 | 薄熙 1185 | 薄一波 1186 | 周小川 1187 | 周建南 1188 | 温云松 1189 | 徐明 1190 | 江泽慧 1191 | 江绵恒 1192 | 江绵康 1193 | 李小鹏 1194 | 李鹏 1195 | 李小琳 1196 | 朱云来 1197 | 朱容基 1198 | 法轮功 1199 | 李洪志 1200 | 新疆骚乱 -------------------------------------------------------------------------------- /core/pool.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type Task func() 10 | 11 | // boss 老板 12 | type GoPool struct { 13 | MaxWorkerIdleTime time.Duration // worker 最大空闲时间 14 | MaxWorkerNum int32 // 协程最大数量 15 | TaskEntryChan chan Task // 任务入列 16 | Workers []*worker // 已创建worker 17 | FreeWorkerChan chan *worker // 空闲worker 18 | Lock sync.Mutex 19 | } 20 | 21 | const ( 22 | WorkerStatusStop = 1 23 | WorkerStatusLive = 0 24 | ) 25 | 26 | // 干活的人 27 | type worker struct { 28 | Pool *GoPool 29 | StartTime time.Time // 开始时间 30 | TaskChan chan Task // 执行队列 31 | LastWorkTime time.Time // 最后执行时间 32 | Ctx context.Context 33 | Cancel context.CancelFunc 34 | Status int32 // 被过期删掉的标记 35 | } 36 | 37 | var defaultPool = func() *GoPool { 38 | return NewPool() 39 | }() 40 | 41 | // 初始化 42 | func NewPool() *GoPool { 43 | g := &GoPool{ 44 | MaxWorkerIdleTime: 10 * time.Second, 45 | MaxWorkerNum: 1000, 46 | TaskEntryChan: make(chan Task, 2000), 47 | FreeWorkerChan: make(chan *worker, 2000), 48 | } 49 | 50 | // 分发任务 51 | go g.dispatchTask() 52 | 53 | //清理空闲worker 54 | go g.fireWorker() 55 | 56 | return g 57 | } 58 | 59 | // 定期清理空闲worker 60 | func (g *GoPool) fireWorker() { 61 | for { 62 | select { 63 | // 10秒执行一次 64 | case <-time.After(10 * time.Second): 65 | for _, w := range g.Workers { 66 | if time.Now().Sub(w.LastWorkTime) > g.MaxWorkerIdleTime { 67 | // 终止协程,但是这个时候,可能任务还是在执行中,执行超时,这时候<-Done会被阻塞 68 | w.Cancel() 69 | // 清理Free 70 | w.Status = WorkerStatusStop 71 | // 上面两步,会worker执行完任务后,就被释放 72 | } 73 | } 74 | 75 | g.Lock.Lock() 76 | g.Workers = g.cleanWorker(g.Workers) 77 | g.Lock.Unlock() 78 | } 79 | } 80 | } 81 | 82 | // 递归清理无用worker 83 | func (g *GoPool) cleanWorker(workers []*worker) []*worker { 84 | for k, w := range workers { 85 | if time.Now().Sub(w.LastWorkTime) > g.MaxWorkerIdleTime { 86 | workers = append(workers[:k], workers[k+1:]...) // 删除中间1个元素 87 | return g.cleanWorker(workers) 88 | } 89 | } 90 | 91 | return workers 92 | } 93 | 94 | // 分发任务 95 | func (g *GoPool) dispatchTask() { 96 | 97 | for { 98 | select { 99 | case t := <-g.TaskEntryChan: 100 | // 获取worker 101 | w := g.fetchWorker() 102 | // 将任务扔给worker 103 | w.accept(t) 104 | } 105 | } 106 | } 107 | 108 | // 获取可用worker 109 | func (g *GoPool) fetchWorker() *worker { 110 | for { 111 | select { 112 | // 获取空闲worker 113 | case w := <-g.FreeWorkerChan: 114 | if w.Status == WorkerStatusLive { 115 | return w 116 | } 117 | default: 118 | // 创建新的worker 119 | if int32(len(g.Workers)) < g.MaxWorkerNum { 120 | w := &worker{ 121 | Pool: g, 122 | StartTime: time.Now(), 123 | LastWorkTime: time.Now(), 124 | TaskChan: make(chan Task, 1), 125 | Ctx: context.Background(), 126 | Status: WorkerStatusLive, 127 | } 128 | ctx, cancel := context.WithCancel(w.Ctx) 129 | 130 | w.Cancel = cancel 131 | // 接到任务自己去执行吧 132 | go w.execute(ctx) 133 | 134 | g.Lock.Lock() 135 | g.Workers = append(g.Workers, w) 136 | g.Lock.Unlock() 137 | 138 | g.FreeWorkerChan <- w 139 | } 140 | } 141 | } 142 | } 143 | 144 | // 添加任务 145 | func (g *GoPool) addTask(t Task) { 146 | // 将任务放到入口任务队列 147 | g.TaskEntryChan <- t 148 | } 149 | 150 | // 接受任务 151 | func (w *worker) accept(t Task) { 152 | // 每个worker自己的工作队列 153 | w.TaskChan <- t 154 | } 155 | 156 | // 执行任务 157 | func (w *worker) execute(ctx context.Context) { 158 | for { 159 | select { 160 | case t := <-w.TaskChan: 161 | // 执行,如果任务执行时间很长,那么会阻塞下一个case 162 | t() 163 | // 记录工作状态 164 | w.LastWorkTime = time.Now() 165 | w.Pool.FreeWorkerChan <- w 166 | case <-ctx.Done(): 167 | return 168 | } 169 | } 170 | } 171 | 172 | // 执行 173 | func SafeGo(t Task) { 174 | defaultPool.addTask(t) 175 | } 176 | -------------------------------------------------------------------------------- /core/service.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "html" 7 | "log" 8 | "net/http" 9 | "sync" 10 | 11 | "github.com/golang/protobuf/proto" 12 | "github.com/gorilla/websocket" 13 | "github.com/sunshinev/go-space-chat/component" 14 | pb "github.com/sunshinev/go-space-chat/proto/star" 15 | ) 16 | 17 | // Core 核心处理 18 | type Core struct { 19 | SocketAddr string 20 | WebAddr string 21 | WebsocketUpgrade websocket.Upgrader 22 | ConnMutex sync.RWMutex 23 | Clients sync.Map // 客户端集合 24 | TextSafer component.TextSafe 25 | loginChart *component.LoginChart 26 | IpSearch *component.IpSearch 27 | } 28 | 29 | // NewCore ... 30 | func NewCore() *Core { 31 | return &Core{} 32 | } 33 | 34 | // 广播消息缓冲通道 35 | var messages = make(chan *pb.BotStatusRequest, 1000) 36 | 37 | func (s *Core) Run() { 38 | // 启动参数 39 | s.SocketAddr = *flag.String("socket_addr", ":9000", "socket address") 40 | s.WebAddr = *flag.String("web_addr", ":80", "http service address") 41 | 42 | flag.Parse() 43 | 44 | log.Printf("socket port %s", s.SocketAddr) 45 | log.Printf("web port %s", s.WebAddr) 46 | 47 | // 敏感词初始化 48 | err := s.TextSafer.NewFilter() 49 | if err != nil { 50 | log.Fatalf("text safe new err %v", err) 51 | } 52 | // 初始日志记录 53 | s.loginChart = component.InitLoginChart() 54 | // 初始化ip转换 55 | s.IpSearch = component.InitIpSearch() 56 | 57 | // 启动web服务 58 | SafeGo(func() { 59 | http.HandleFunc("/login_charts", s.ChartDataApi) 60 | http.Handle("/", http.FileServer(http.Dir("web_resource/dist/"))) 61 | 62 | err := http.ListenAndServe(s.WebAddr, nil) 63 | if err != nil { 64 | log.Fatalf("web 服务启动失败 %v", err) 65 | } else { 66 | log.Printf("web 服务启动成功 端口 %s", s.WebAddr) 67 | } 68 | }) 69 | 70 | // 广播 71 | SafeGo(func() { 72 | s.broadcast() 73 | }) 74 | // pprof 性能 75 | SafeGo(func() { 76 | log.Println(http.ListenAndServe(":6060", nil)) 77 | }) 78 | 79 | // 监听websocket 80 | http.HandleFunc("/ws", s.websocketUpgrade) 81 | 82 | err = http.ListenAndServe(s.SocketAddr, nil) 83 | if err != nil { 84 | log.Fatalf("create error %v", err) 85 | } 86 | } 87 | 88 | // 升级http为websocket协议 89 | func (s *Core) websocketUpgrade(w http.ResponseWriter, r *http.Request) { 90 | // 跨域 91 | s.WebsocketUpgrade.CheckOrigin = func(r *http.Request) bool { 92 | return true 93 | } 94 | // 升级http为websocket 95 | conn, err := s.WebsocketUpgrade.Upgrade(w, r, nil) 96 | 97 | if err != nil { 98 | log.Printf("http upgrade webcoket err %v", err) 99 | } else { 100 | SafeGo(func() { 101 | s.listenWebsocket(conn) 102 | }) 103 | } 104 | } 105 | 106 | // 监听message消息 107 | func (s *Core) listenWebsocket(conn *websocket.Conn) { 108 | defer func() { 109 | err := conn.Close() 110 | if err != nil { 111 | log.Printf("close websocket err %v", err) 112 | } 113 | }() 114 | // 监听 115 | for { 116 | // 尝试查询当前连接 117 | cInfo, ok := s.Clients.Load(conn) 118 | if !ok { 119 | // 写入空 120 | cInfo = &pb.BotStatusRequest{} 121 | } 122 | clientInfo, ok := cInfo.(*pb.BotStatusRequest) 123 | if !ok { 124 | log.Printf("assert sync map pb.BotStatusRequest err %v", clientInfo) 125 | s.Clients.Delete(conn) 126 | continue 127 | } 128 | // 读取消息 129 | _, message, err := conn.ReadMessage() 130 | if err != nil { 131 | log.Printf("read message error,client: %v break, ip: %v, err:%v", clientInfo.BotId, conn.RemoteAddr(), err) 132 | messages <- &pb.BotStatusRequest{ 133 | BotId: clientInfo.BotId, 134 | Name: clientInfo.Name, 135 | Msg: "我下线了~拜拜~", 136 | PosInfo: clientInfo.PosInfo, 137 | } 138 | // 广播关闭连接 139 | messages <- &pb.BotStatusRequest{ 140 | BotId: clientInfo.BotId, 141 | Status: pb.BotStatusRequest_close, 142 | } 143 | // 清除用户 144 | s.Clients.Delete(conn) 145 | // 关闭连接 146 | err = conn.Close() 147 | if err != nil { 148 | log.Printf("close websocket err %v", err) 149 | } 150 | break 151 | } 152 | // 消息读取成功,解析消息 153 | // 使用protobuf解析 154 | pbr := &pb.BotStatusRequest{} 155 | err = proto.Unmarshal(message, pbr) 156 | if err != nil { 157 | log.Printf("proto parse message %v err %v", message, err) 158 | continue 159 | } 160 | // 敏感词过滤 161 | pbr.Msg = s.TextSafer.Filter(pbr.Msg) 162 | pbr.Name = s.TextSafer.Filter(pbr.Name) 163 | // 过滤html 标签 164 | pbr.Msg = html.EscapeString(pbr.Msg) 165 | pbr.Name = html.EscapeString(pbr.Name) 166 | 167 | // 如果是新用户初始化链接的ID 168 | if clientInfo.BotId == "" { 169 | // 获取地理位置 170 | posInfo := pb.PInfo{} 171 | pinfo, err := s.IpSearch.Search(conn.RemoteAddr().String()) 172 | if err != nil { 173 | log.Printf("ip search err %v", err) 174 | } else { 175 | posInfo = pb.PInfo{ 176 | CityId: int32(pinfo.CityId), 177 | Country: pinfo.Country, 178 | Region: pinfo.Region, 179 | Province: pinfo.Province, 180 | City: pinfo.City, 181 | Isp: pinfo.ISP, 182 | } 183 | } 184 | s.Clients.Store(conn, &pb.BotStatusRequest{ 185 | BotId: pbr.GetBotId(), 186 | Name: pbr.GetName(), 187 | Status: pb.BotStatusRequest_connecting, 188 | PosInfo: &posInfo, 189 | }) 190 | // 新用户进行上线提示 191 | pbr.Msg = "我上线啦~大家好呀" 192 | pbr.PosInfo = &posInfo 193 | // 新用户上线,记录次数 194 | s.loginChart.Entry() 195 | } else { 196 | // 老用户直接从clients获取pos信息 197 | pbr.PosInfo = clientInfo.PosInfo 198 | } 199 | // 广播队列 200 | messages <- pbr 201 | } 202 | } 203 | 204 | // 广播 205 | func (s *Core) broadcast() { 206 | // 始终读取messages 207 | for msg := range messages { 208 | if msg.Msg != "" { 209 | log.Printf("%s : %s", msg.BotId+":"+msg.Name, msg.Msg) 210 | } 211 | 212 | // 读取到之后进行广播,启动协程,是为了立即处理下一条msg 213 | go func(m *pb.BotStatusRequest) { 214 | // 遍历所有客户 215 | s.Clients.Range(func(connKey, bs interface{}) bool { 216 | 217 | resp := &pb.BotStatusResponse{ 218 | BotStatus: []*pb.BotStatusRequest{m}, 219 | } 220 | b, err := proto.Marshal(resp) 221 | if err != nil { 222 | log.Printf("proto marshal error %v %+v", err, resp) 223 | return true 224 | } 225 | 226 | // 二进制发送 227 | conn, ok := connKey.(*websocket.Conn) 228 | if !ok { 229 | log.Printf("assert connkey websocket.Conn err %v", conn) 230 | return true 231 | } 232 | // 防止并发写 233 | s.ConnMutex.Lock() 234 | err = conn.WriteMessage(websocket.BinaryMessage, b) 235 | s.ConnMutex.Unlock() 236 | if err != nil { 237 | log.Printf("conn write message err %v", err) 238 | } 239 | return true 240 | }) 241 | }(msg) 242 | } 243 | } 244 | 245 | type ChartApiRsp struct { 246 | X []string `json:"x"` 247 | Y []int32 `json:"y"` 248 | } 249 | 250 | // ChartDataApi 统计了一天内在线人数趋势 251 | func (s *Core) ChartDataApi(w http.ResponseWriter, r *http.Request) { 252 | chartData := s.loginChart.FetchAllData() 253 | 254 | xSlice := []string{} 255 | ySlice := []int32{} 256 | 257 | for _, v := range chartData { 258 | xSlice = append(xSlice, v.X) 259 | ySlice = append(ySlice, v.Y) 260 | } 261 | 262 | data := &ChartApiRsp{ 263 | X: xSlice, 264 | Y: ySlice, 265 | } 266 | 267 | d, err := json.Marshal(data) 268 | if err != nil { 269 | log.Printf("ChartDataApi marsharl %v", err) 270 | return 271 | } 272 | 273 | w.Header().Set("content-type", "application/json") 274 | _, err = w.Write(d) 275 | if err != nil { 276 | log.Printf("ChartDataApi write %v", err) 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | my_debian_container: 5 | image: debian:latest 6 | tty: true # 保持控制台连接,方便执行命令 7 | 8 | ports: 9 | - "8081:80" # 映射容器的8080端口到主机的8080端口 10 | - "9001:9000" # 映射容器的3000端口到主机的3000端口 11 | 12 | # 安装依赖的软件包和Go语言 13 | command: > 14 | bash -c " 15 | apt-get update && 16 | apt-get install -y wget git && 17 | wget https://golang.org/dl/go1.17.2.linux-arm64.tar.gz && 18 | tar -C /usr/local -xzf go1.17.2.linux-arm64.tar.gz && 19 | export PATH=$PATH:/usr/local/go/bin && 20 | go version && 21 | cd ~ && 22 | git clone https://github.com/sunshinev/go-space-chat.git && 23 | cd go-space-chat && 24 | go run main.go # 在 Git 项目的根目录下运行 Go 代码 25 | " 26 | 27 | environment: 28 | - GO111MODULE=on # 设置Go的模块支持 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/sunshinev/go-space-chat 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/antlinker/go-cmap v0.0.0-20160407022646-0c5e57012e96 // indirect 7 | github.com/antlinker/go-dirtyfilter v1.2.0 8 | github.com/golang/protobuf v1.4.0 9 | github.com/gorilla/websocket v1.4.2 10 | github.com/lionsoul2014/ip2region v2.2.0-release+incompatible 11 | google.golang.org/protobuf v1.21.0 12 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/antlinker/go-cmap v0.0.0-20160407022646-0c5e57012e96 h1:9jCOqZ1UyRwI5JPMUuYnIpLNgBPcsRXsjH0JZTDbvts= 2 | github.com/antlinker/go-cmap v0.0.0-20160407022646-0c5e57012e96/go.mod h1:G+LGOmf0CtTskZRVr2cOGafQmsphVLDPfOIqAXGOTQI= 3 | github.com/antlinker/go-dirtyfilter v1.2.0 h1:4r4fREWbL+vQaB65dCxYSzG679MqFUKtSKIE1S4qt38= 4 | github.com/antlinker/go-dirtyfilter v1.2.0/go.mod h1:QQqzUFiff9pyPiL1SnK9T3JELk74iXSMZL3/iHUbEWA= 5 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 6 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 7 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 8 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 9 | github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= 10 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 11 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 12 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 13 | github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= 14 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 15 | github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= 16 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 17 | github.com/lionsoul2014/ip2region v2.2.0-release+incompatible h1:1qp9iks+69h7IGLazAplzS9Ca14HAxuD5c0rbFdPGy4= 18 | github.com/lionsoul2014/ip2region v2.2.0-release+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI= 19 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 20 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 21 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 22 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 23 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 24 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 25 | google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw= 26 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 27 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= 28 | gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= 29 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | _ "net/http/pprof" 6 | 7 | "github.com/sunshinev/go-space-chat/core" 8 | ) 9 | 10 | func main() { 11 | log.SetFlags(log.Lshortfile | log.LstdFlags) 12 | core.NewCore().Run() 13 | } 14 | -------------------------------------------------------------------------------- /proto/star/star.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.25.0 4 | // protoc v3.11.4 5 | // source: star.proto 6 | 7 | package star 8 | 9 | import ( 10 | proto "github.com/golang/protobuf/proto" 11 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 12 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 13 | reflect "reflect" 14 | sync "sync" 15 | ) 16 | 17 | const ( 18 | // Verify that this generated code is sufficiently up-to-date. 19 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 | // Verify that runtime/protoimpl is sufficiently up-to-date. 21 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 | ) 23 | 24 | // This is a compile-time assertion that a sufficiently up-to-date version 25 | // of the legacy proto package is being used. 26 | const _ = proto.ProtoPackageIsVersion4 27 | 28 | type BotStatusRequestStatusType int32 29 | 30 | const ( 31 | BotStatusRequest_waiting BotStatusRequestStatusType = 0 32 | BotStatusRequest_connecting BotStatusRequestStatusType = 1 33 | BotStatusRequest_close BotStatusRequestStatusType = 2 34 | ) 35 | 36 | // Enum value maps for BotStatusRequestStatusType. 37 | var ( 38 | BotStatusRequestStatusType_name = map[int32]string{ 39 | 0: "waiting", 40 | 1: "connecting", 41 | 2: "close", 42 | } 43 | BotStatusRequestStatusType_value = map[string]int32{ 44 | "waiting": 0, 45 | "connecting": 1, 46 | "close": 2, 47 | } 48 | ) 49 | 50 | func (x BotStatusRequestStatusType) Enum() *BotStatusRequestStatusType { 51 | p := new(BotStatusRequestStatusType) 52 | *p = x 53 | return p 54 | } 55 | 56 | func (x BotStatusRequestStatusType) String() string { 57 | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) 58 | } 59 | 60 | func (BotStatusRequestStatusType) Descriptor() protoreflect.EnumDescriptor { 61 | return file_star_proto_enumTypes[0].Descriptor() 62 | } 63 | 64 | func (BotStatusRequestStatusType) Type() protoreflect.EnumType { 65 | return &file_star_proto_enumTypes[0] 66 | } 67 | 68 | func (x BotStatusRequestStatusType) Number() protoreflect.EnumNumber { 69 | return protoreflect.EnumNumber(x) 70 | } 71 | 72 | // Deprecated: Use BotStatusRequestStatusType.Descriptor instead. 73 | func (BotStatusRequestStatusType) EnumDescriptor() ([]byte, []int) { 74 | return file_star_proto_rawDescGZIP(), []int{1, 0} 75 | } 76 | 77 | type BotStatusRequestGenderType int32 78 | 79 | const ( 80 | BotStatusRequest_man BotStatusRequestGenderType = 0 81 | BotStatusRequest_woman BotStatusRequestGenderType = 1 82 | ) 83 | 84 | // Enum value maps for BotStatusRequestGenderType. 85 | var ( 86 | BotStatusRequestGenderType_name = map[int32]string{ 87 | 0: "man", 88 | 1: "woman", 89 | } 90 | BotStatusRequestGenderType_value = map[string]int32{ 91 | "man": 0, 92 | "woman": 1, 93 | } 94 | ) 95 | 96 | func (x BotStatusRequestGenderType) Enum() *BotStatusRequestGenderType { 97 | p := new(BotStatusRequestGenderType) 98 | *p = x 99 | return p 100 | } 101 | 102 | func (x BotStatusRequestGenderType) String() string { 103 | return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) 104 | } 105 | 106 | func (BotStatusRequestGenderType) Descriptor() protoreflect.EnumDescriptor { 107 | return file_star_proto_enumTypes[1].Descriptor() 108 | } 109 | 110 | func (BotStatusRequestGenderType) Type() protoreflect.EnumType { 111 | return &file_star_proto_enumTypes[1] 112 | } 113 | 114 | func (x BotStatusRequestGenderType) Number() protoreflect.EnumNumber { 115 | return protoreflect.EnumNumber(x) 116 | } 117 | 118 | // Deprecated: Use BotStatusRequestGenderType.Descriptor instead. 119 | func (BotStatusRequestGenderType) EnumDescriptor() ([]byte, []int) { 120 | return file_star_proto_rawDescGZIP(), []int{1, 1} 121 | } 122 | 123 | type PInfo struct { 124 | state protoimpl.MessageState 125 | sizeCache protoimpl.SizeCache 126 | unknownFields protoimpl.UnknownFields 127 | 128 | CityId int32 `protobuf:"varint,1,opt,name=city_id,json=cityId,proto3" json:"city_id,omitempty"` 129 | Country string `protobuf:"bytes,2,opt,name=country,proto3" json:"country,omitempty"` 130 | Region string `protobuf:"bytes,3,opt,name=region,proto3" json:"region,omitempty"` 131 | Province string `protobuf:"bytes,4,opt,name=province,proto3" json:"province,omitempty"` 132 | City string `protobuf:"bytes,5,opt,name=city,proto3" json:"city,omitempty"` 133 | Isp string `protobuf:"bytes,6,opt,name=isp,proto3" json:"isp,omitempty"` 134 | } 135 | 136 | func (x *PInfo) Reset() { 137 | *x = PInfo{} 138 | if protoimpl.UnsafeEnabled { 139 | mi := &file_star_proto_msgTypes[0] 140 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 141 | ms.StoreMessageInfo(mi) 142 | } 143 | } 144 | 145 | func (x *PInfo) String() string { 146 | return protoimpl.X.MessageStringOf(x) 147 | } 148 | 149 | func (*PInfo) ProtoMessage() {} 150 | 151 | func (x *PInfo) ProtoReflect() protoreflect.Message { 152 | mi := &file_star_proto_msgTypes[0] 153 | if protoimpl.UnsafeEnabled && x != nil { 154 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 155 | if ms.LoadMessageInfo() == nil { 156 | ms.StoreMessageInfo(mi) 157 | } 158 | return ms 159 | } 160 | return mi.MessageOf(x) 161 | } 162 | 163 | // Deprecated: Use PInfo.ProtoReflect.Descriptor instead. 164 | func (*PInfo) Descriptor() ([]byte, []int) { 165 | return file_star_proto_rawDescGZIP(), []int{0} 166 | } 167 | 168 | func (x *PInfo) GetCityId() int32 { 169 | if x != nil { 170 | return x.CityId 171 | } 172 | return 0 173 | } 174 | 175 | func (x *PInfo) GetCountry() string { 176 | if x != nil { 177 | return x.Country 178 | } 179 | return "" 180 | } 181 | 182 | func (x *PInfo) GetRegion() string { 183 | if x != nil { 184 | return x.Region 185 | } 186 | return "" 187 | } 188 | 189 | func (x *PInfo) GetProvince() string { 190 | if x != nil { 191 | return x.Province 192 | } 193 | return "" 194 | } 195 | 196 | func (x *PInfo) GetCity() string { 197 | if x != nil { 198 | return x.City 199 | } 200 | return "" 201 | } 202 | 203 | func (x *PInfo) GetIsp() string { 204 | if x != nil { 205 | return x.Isp 206 | } 207 | return "" 208 | } 209 | 210 | type BotStatusRequest struct { 211 | state protoimpl.MessageState 212 | sizeCache protoimpl.SizeCache 213 | unknownFields protoimpl.UnknownFields 214 | 215 | BotId string `protobuf:"bytes,1,opt,name=bot_id,json=botId,proto3" json:"bot_id,omitempty"` 216 | X float32 `protobuf:"fixed32,2,opt,name=x,proto3" json:"x,omitempty"` 217 | Y float32 `protobuf:"fixed32,3,opt,name=y,proto3" json:"y,omitempty"` 218 | EyeX float32 `protobuf:"fixed32,4,opt,name=eye_x,json=eyeX,proto3" json:"eye_x,omitempty"` 219 | EyeY float32 `protobuf:"fixed32,5,opt,name=eye_y,json=eyeY,proto3" json:"eye_y,omitempty"` 220 | Msg string `protobuf:"bytes,6,opt,name=msg,proto3" json:"msg,omitempty"` 221 | RealX float32 `protobuf:"fixed32,7,opt,name=real_x,json=realX,proto3" json:"real_x,omitempty"` 222 | RealY float32 `protobuf:"fixed32,8,opt,name=real_y,json=realY,proto3" json:"real_y,omitempty"` 223 | Status BotStatusRequestStatusType `protobuf:"varint,9,opt,name=status,proto3,enum=BotStatusRequestStatusType" json:"status,omitempty"` 224 | Name string `protobuf:"bytes,10,opt,name=name,proto3" json:"name,omitempty"` 225 | Gender BotStatusRequestGenderType `protobuf:"varint,11,opt,name=gender,proto3,enum=BotStatusRequestGenderType" json:"gender,omitempty"` 226 | PosInfo *PInfo `protobuf:"bytes,12,opt,name=pos_info,json=posInfo,proto3" json:"pos_info,omitempty"` 227 | } 228 | 229 | func (x *BotStatusRequest) Reset() { 230 | *x = BotStatusRequest{} 231 | if protoimpl.UnsafeEnabled { 232 | mi := &file_star_proto_msgTypes[1] 233 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 234 | ms.StoreMessageInfo(mi) 235 | } 236 | } 237 | 238 | func (x *BotStatusRequest) String() string { 239 | return protoimpl.X.MessageStringOf(x) 240 | } 241 | 242 | func (*BotStatusRequest) ProtoMessage() {} 243 | 244 | func (x *BotStatusRequest) ProtoReflect() protoreflect.Message { 245 | mi := &file_star_proto_msgTypes[1] 246 | if protoimpl.UnsafeEnabled && x != nil { 247 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 248 | if ms.LoadMessageInfo() == nil { 249 | ms.StoreMessageInfo(mi) 250 | } 251 | return ms 252 | } 253 | return mi.MessageOf(x) 254 | } 255 | 256 | // Deprecated: Use BotStatusRequest.ProtoReflect.Descriptor instead. 257 | func (*BotStatusRequest) Descriptor() ([]byte, []int) { 258 | return file_star_proto_rawDescGZIP(), []int{1} 259 | } 260 | 261 | func (x *BotStatusRequest) GetBotId() string { 262 | if x != nil { 263 | return x.BotId 264 | } 265 | return "" 266 | } 267 | 268 | func (x *BotStatusRequest) GetX() float32 { 269 | if x != nil { 270 | return x.X 271 | } 272 | return 0 273 | } 274 | 275 | func (x *BotStatusRequest) GetY() float32 { 276 | if x != nil { 277 | return x.Y 278 | } 279 | return 0 280 | } 281 | 282 | func (x *BotStatusRequest) GetEyeX() float32 { 283 | if x != nil { 284 | return x.EyeX 285 | } 286 | return 0 287 | } 288 | 289 | func (x *BotStatusRequest) GetEyeY() float32 { 290 | if x != nil { 291 | return x.EyeY 292 | } 293 | return 0 294 | } 295 | 296 | func (x *BotStatusRequest) GetMsg() string { 297 | if x != nil { 298 | return x.Msg 299 | } 300 | return "" 301 | } 302 | 303 | func (x *BotStatusRequest) GetRealX() float32 { 304 | if x != nil { 305 | return x.RealX 306 | } 307 | return 0 308 | } 309 | 310 | func (x *BotStatusRequest) GetRealY() float32 { 311 | if x != nil { 312 | return x.RealY 313 | } 314 | return 0 315 | } 316 | 317 | func (x *BotStatusRequest) GetStatus() BotStatusRequestStatusType { 318 | if x != nil { 319 | return x.Status 320 | } 321 | return BotStatusRequest_waiting 322 | } 323 | 324 | func (x *BotStatusRequest) GetName() string { 325 | if x != nil { 326 | return x.Name 327 | } 328 | return "" 329 | } 330 | 331 | func (x *BotStatusRequest) GetGender() BotStatusRequestGenderType { 332 | if x != nil { 333 | return x.Gender 334 | } 335 | return BotStatusRequest_man 336 | } 337 | 338 | func (x *BotStatusRequest) GetPosInfo() *PInfo { 339 | if x != nil { 340 | return x.PosInfo 341 | } 342 | return nil 343 | } 344 | 345 | type BotStatusResponse struct { 346 | state protoimpl.MessageState 347 | sizeCache protoimpl.SizeCache 348 | unknownFields protoimpl.UnknownFields 349 | 350 | BotStatus []*BotStatusRequest `protobuf:"bytes,1,rep,name=bot_status,json=botStatus,proto3" json:"bot_status,omitempty"` 351 | } 352 | 353 | func (x *BotStatusResponse) Reset() { 354 | *x = BotStatusResponse{} 355 | if protoimpl.UnsafeEnabled { 356 | mi := &file_star_proto_msgTypes[2] 357 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 358 | ms.StoreMessageInfo(mi) 359 | } 360 | } 361 | 362 | func (x *BotStatusResponse) String() string { 363 | return protoimpl.X.MessageStringOf(x) 364 | } 365 | 366 | func (*BotStatusResponse) ProtoMessage() {} 367 | 368 | func (x *BotStatusResponse) ProtoReflect() protoreflect.Message { 369 | mi := &file_star_proto_msgTypes[2] 370 | if protoimpl.UnsafeEnabled && x != nil { 371 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 372 | if ms.LoadMessageInfo() == nil { 373 | ms.StoreMessageInfo(mi) 374 | } 375 | return ms 376 | } 377 | return mi.MessageOf(x) 378 | } 379 | 380 | // Deprecated: Use BotStatusResponse.ProtoReflect.Descriptor instead. 381 | func (*BotStatusResponse) Descriptor() ([]byte, []int) { 382 | return file_star_proto_rawDescGZIP(), []int{2} 383 | } 384 | 385 | func (x *BotStatusResponse) GetBotStatus() []*BotStatusRequest { 386 | if x != nil { 387 | return x.BotStatus 388 | } 389 | return nil 390 | } 391 | 392 | var File_star_proto protoreflect.FileDescriptor 393 | 394 | var file_star_proto_rawDesc = []byte{ 395 | 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, 0x01, 0x0a, 396 | 0x05, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 397 | 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x63, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 398 | 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 399 | 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 400 | 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 401 | 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 402 | 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 403 | 0x04, 0x63, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, 404 | 0x79, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x73, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 405 | 0x69, 0x73, 0x70, 0x22, 0xae, 0x03, 0x0a, 0x10, 0x62, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 406 | 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x62, 0x6f, 0x74, 0x5f, 407 | 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x62, 0x6f, 0x74, 0x49, 0x64, 0x12, 408 | 0x0c, 0x0a, 0x01, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x01, 0x78, 0x12, 0x0c, 0x0a, 409 | 0x01, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x01, 0x79, 0x12, 0x13, 0x0a, 0x05, 0x65, 410 | 0x79, 0x65, 0x5f, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x02, 0x52, 0x04, 0x65, 0x79, 0x65, 0x58, 411 | 0x12, 0x13, 0x0a, 0x05, 0x65, 0x79, 0x65, 0x5f, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x02, 0x52, 412 | 0x04, 0x65, 0x79, 0x65, 0x59, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x06, 0x20, 0x01, 413 | 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x15, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x6c, 0x5f, 414 | 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x72, 0x65, 0x61, 0x6c, 0x58, 0x12, 0x15, 415 | 0x0a, 0x06, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 416 | 0x72, 0x65, 0x61, 0x6c, 0x59, 0x12, 0x35, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 417 | 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x62, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 418 | 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 419 | 0x74, 0x79, 0x70, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 420 | 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 421 | 0x12, 0x35, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 422 | 0x32, 0x1d, 0x2e, 0x62, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 423 | 0x65, 0x73, 0x74, 0x2e, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 424 | 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x08, 0x70, 0x6f, 0x73, 0x5f, 0x69, 425 | 0x6e, 0x66, 0x6f, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x70, 0x49, 0x6e, 0x66, 426 | 0x6f, 0x52, 0x07, 0x70, 0x6f, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x35, 0x0a, 0x0b, 0x73, 0x74, 427 | 0x61, 0x74, 0x75, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x77, 0x61, 0x69, 428 | 0x74, 0x69, 0x6e, 0x67, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 429 | 0x74, 0x69, 0x6e, 0x67, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0x10, 430 | 0x02, 0x22, 0x21, 0x0a, 0x0b, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x74, 0x79, 0x70, 0x65, 431 | 0x12, 0x07, 0x0a, 0x03, 0x6d, 0x61, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x77, 0x6f, 0x6d, 432 | 0x61, 0x6e, 0x10, 0x01, 0x22, 0x45, 0x0a, 0x11, 0x62, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 433 | 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x0a, 0x62, 0x6f, 0x74, 434 | 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 435 | 0x62, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 436 | 0x52, 0x09, 0x62, 0x6f, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x08, 0x5a, 0x06, 0x2e, 437 | 0x3b, 0x73, 0x74, 0x61, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 438 | } 439 | 440 | var ( 441 | file_star_proto_rawDescOnce sync.Once 442 | file_star_proto_rawDescData = file_star_proto_rawDesc 443 | ) 444 | 445 | func file_star_proto_rawDescGZIP() []byte { 446 | file_star_proto_rawDescOnce.Do(func() { 447 | file_star_proto_rawDescData = protoimpl.X.CompressGZIP(file_star_proto_rawDescData) 448 | }) 449 | return file_star_proto_rawDescData 450 | } 451 | 452 | var file_star_proto_enumTypes = make([]protoimpl.EnumInfo, 2) 453 | var file_star_proto_msgTypes = make([]protoimpl.MessageInfo, 3) 454 | var file_star_proto_goTypes = []interface{}{ 455 | (BotStatusRequestStatusType)(0), // 0: botStatusRequest.status_type 456 | (BotStatusRequestGenderType)(0), // 1: botStatusRequest.gender_type 457 | (*PInfo)(nil), // 2: pInfo 458 | (*BotStatusRequest)(nil), // 3: botStatusRequest 459 | (*BotStatusResponse)(nil), // 4: botStatusResponse 460 | } 461 | var file_star_proto_depIdxs = []int32{ 462 | 0, // 0: botStatusRequest.status:type_name -> botStatusRequest.status_type 463 | 1, // 1: botStatusRequest.gender:type_name -> botStatusRequest.gender_type 464 | 2, // 2: botStatusRequest.pos_info:type_name -> pInfo 465 | 3, // 3: botStatusResponse.bot_status:type_name -> botStatusRequest 466 | 4, // [4:4] is the sub-list for method output_type 467 | 4, // [4:4] is the sub-list for method input_type 468 | 4, // [4:4] is the sub-list for extension type_name 469 | 4, // [4:4] is the sub-list for extension extendee 470 | 0, // [0:4] is the sub-list for field type_name 471 | } 472 | 473 | func init() { file_star_proto_init() } 474 | func file_star_proto_init() { 475 | if File_star_proto != nil { 476 | return 477 | } 478 | if !protoimpl.UnsafeEnabled { 479 | file_star_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 480 | switch v := v.(*PInfo); i { 481 | case 0: 482 | return &v.state 483 | case 1: 484 | return &v.sizeCache 485 | case 2: 486 | return &v.unknownFields 487 | default: 488 | return nil 489 | } 490 | } 491 | file_star_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 492 | switch v := v.(*BotStatusRequest); i { 493 | case 0: 494 | return &v.state 495 | case 1: 496 | return &v.sizeCache 497 | case 2: 498 | return &v.unknownFields 499 | default: 500 | return nil 501 | } 502 | } 503 | file_star_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 504 | switch v := v.(*BotStatusResponse); i { 505 | case 0: 506 | return &v.state 507 | case 1: 508 | return &v.sizeCache 509 | case 2: 510 | return &v.unknownFields 511 | default: 512 | return nil 513 | } 514 | } 515 | } 516 | type x struct{} 517 | out := protoimpl.TypeBuilder{ 518 | File: protoimpl.DescBuilder{ 519 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 520 | RawDescriptor: file_star_proto_rawDesc, 521 | NumEnums: 2, 522 | NumMessages: 3, 523 | NumExtensions: 0, 524 | NumServices: 0, 525 | }, 526 | GoTypes: file_star_proto_goTypes, 527 | DependencyIndexes: file_star_proto_depIdxs, 528 | EnumInfos: file_star_proto_enumTypes, 529 | MessageInfos: file_star_proto_msgTypes, 530 | }.Build() 531 | File_star_proto = out.File 532 | file_star_proto_rawDesc = nil 533 | file_star_proto_goTypes = nil 534 | file_star_proto_depIdxs = nil 535 | } 536 | -------------------------------------------------------------------------------- /proto/star/star.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = ".;star"; 4 | 5 | message pInfo { 6 | int32 city_id = 1; 7 | string country = 2; 8 | string region = 3; 9 | string province = 4; 10 | string city = 5; 11 | string isp = 6; 12 | } 13 | 14 | message botStatusRequest { 15 | string bot_id = 1; 16 | float x = 2; 17 | float y = 3; 18 | float eye_x = 4; 19 | float eye_y = 5; 20 | string msg = 6; 21 | float real_x = 7; 22 | float real_y = 8; 23 | 24 | enum status_type { 25 | waiting = 0; 26 | connecting = 1; 27 | close = 2; 28 | } 29 | 30 | status_type status = 9; 31 | string name = 10; 32 | 33 | enum gender_type { 34 | man = 0; 35 | woman = 1; 36 | } 37 | 38 | gender_type gender = 11; 39 | pInfo pos_info = 12; 40 | } 41 | 42 | message botStatusResponse { 43 | repeated botStatusRequest bot_status = 1; 44 | } -------------------------------------------------------------------------------- /proto/star/star_pb.js: -------------------------------------------------------------------------------- 1 | // source: star.proto 2 | /** 3 | * @fileoverview 4 | * @enhanceable 5 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 6 | * field starts with 'MSG_' and isn't a translatable message. 7 | * @public 8 | */ 9 | // GENERATED CODE -- DO NOT EDIT! 10 | 11 | var jspb = require('google-protobuf'); 12 | var goog = jspb; 13 | var global = Function('return this')(); 14 | 15 | goog.exportSymbol('proto.botStatusRequest', null, global); 16 | goog.exportSymbol('proto.botStatusRequest.gender_type', null, global); 17 | goog.exportSymbol('proto.botStatusRequest.status_type', null, global); 18 | goog.exportSymbol('proto.botStatusResponse', null, global); 19 | goog.exportSymbol('proto.pInfo', null, global); 20 | /** 21 | * Generated by JsPbCodeGenerator. 22 | * @param {Array=} opt_data Optional initial data array, typically from a 23 | * server response, or constructed directly in Javascript. The array is used 24 | * in place and becomes part of the constructed object. It is not cloned. 25 | * If no data is provided, the constructed object will be empty, but still 26 | * valid. 27 | * @extends {jspb.Message} 28 | * @constructor 29 | */ 30 | proto.pInfo = function(opt_data) { 31 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 32 | }; 33 | goog.inherits(proto.pInfo, jspb.Message); 34 | if (goog.DEBUG && !COMPILED) { 35 | /** 36 | * @public 37 | * @override 38 | */ 39 | proto.pInfo.displayName = 'proto.pInfo'; 40 | } 41 | /** 42 | * Generated by JsPbCodeGenerator. 43 | * @param {Array=} opt_data Optional initial data array, typically from a 44 | * server response, or constructed directly in Javascript. The array is used 45 | * in place and becomes part of the constructed object. It is not cloned. 46 | * If no data is provided, the constructed object will be empty, but still 47 | * valid. 48 | * @extends {jspb.Message} 49 | * @constructor 50 | */ 51 | proto.botStatusRequest = function(opt_data) { 52 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 53 | }; 54 | goog.inherits(proto.botStatusRequest, jspb.Message); 55 | if (goog.DEBUG && !COMPILED) { 56 | /** 57 | * @public 58 | * @override 59 | */ 60 | proto.botStatusRequest.displayName = 'proto.botStatusRequest'; 61 | } 62 | /** 63 | * Generated by JsPbCodeGenerator. 64 | * @param {Array=} opt_data Optional initial data array, typically from a 65 | * server response, or constructed directly in Javascript. The array is used 66 | * in place and becomes part of the constructed object. It is not cloned. 67 | * If no data is provided, the constructed object will be empty, but still 68 | * valid. 69 | * @extends {jspb.Message} 70 | * @constructor 71 | */ 72 | proto.botStatusResponse = function(opt_data) { 73 | jspb.Message.initialize(this, opt_data, 0, -1, proto.botStatusResponse.repeatedFields_, null); 74 | }; 75 | goog.inherits(proto.botStatusResponse, jspb.Message); 76 | if (goog.DEBUG && !COMPILED) { 77 | /** 78 | * @public 79 | * @override 80 | */ 81 | proto.botStatusResponse.displayName = 'proto.botStatusResponse'; 82 | } 83 | 84 | 85 | 86 | if (jspb.Message.GENERATE_TO_OBJECT) { 87 | /** 88 | * Creates an object representation of this proto. 89 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 90 | * Optional fields that are not set will be set to undefined. 91 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 92 | * For the list of reserved names please see: 93 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 94 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 95 | * JSPB instance for transitional soy proto support: 96 | * http://goto/soy-param-migration 97 | * @return {!Object} 98 | */ 99 | proto.pInfo.prototype.toObject = function(opt_includeInstance) { 100 | return proto.pInfo.toObject(opt_includeInstance, this); 101 | }; 102 | 103 | 104 | /** 105 | * Static version of the {@see toObject} method. 106 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 107 | * the JSPB instance for transitional soy proto support: 108 | * http://goto/soy-param-migration 109 | * @param {!proto.pInfo} msg The msg instance to transform. 110 | * @return {!Object} 111 | * @suppress {unusedLocalVariables} f is only used for nested messages 112 | */ 113 | proto.pInfo.toObject = function(includeInstance, msg) { 114 | var f, obj = { 115 | cityId: jspb.Message.getFieldWithDefault(msg, 1, 0), 116 | country: jspb.Message.getFieldWithDefault(msg, 2, ""), 117 | region: jspb.Message.getFieldWithDefault(msg, 3, ""), 118 | province: jspb.Message.getFieldWithDefault(msg, 4, ""), 119 | city: jspb.Message.getFieldWithDefault(msg, 5, ""), 120 | isp: jspb.Message.getFieldWithDefault(msg, 6, "") 121 | }; 122 | 123 | if (includeInstance) { 124 | obj.$jspbMessageInstance = msg; 125 | } 126 | return obj; 127 | }; 128 | } 129 | 130 | 131 | /** 132 | * Deserializes binary data (in protobuf wire format). 133 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 134 | * @return {!proto.pInfo} 135 | */ 136 | proto.pInfo.deserializeBinary = function(bytes) { 137 | var reader = new jspb.BinaryReader(bytes); 138 | var msg = new proto.pInfo; 139 | return proto.pInfo.deserializeBinaryFromReader(msg, reader); 140 | }; 141 | 142 | 143 | /** 144 | * Deserializes binary data (in protobuf wire format) from the 145 | * given reader into the given message object. 146 | * @param {!proto.pInfo} msg The message object to deserialize into. 147 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 148 | * @return {!proto.pInfo} 149 | */ 150 | proto.pInfo.deserializeBinaryFromReader = function(msg, reader) { 151 | while (reader.nextField()) { 152 | if (reader.isEndGroup()) { 153 | break; 154 | } 155 | var field = reader.getFieldNumber(); 156 | switch (field) { 157 | case 1: 158 | var value = /** @type {number} */ (reader.readInt32()); 159 | msg.setCityId(value); 160 | break; 161 | case 2: 162 | var value = /** @type {string} */ (reader.readString()); 163 | msg.setCountry(value); 164 | break; 165 | case 3: 166 | var value = /** @type {string} */ (reader.readString()); 167 | msg.setRegion(value); 168 | break; 169 | case 4: 170 | var value = /** @type {string} */ (reader.readString()); 171 | msg.setProvince(value); 172 | break; 173 | case 5: 174 | var value = /** @type {string} */ (reader.readString()); 175 | msg.setCity(value); 176 | break; 177 | case 6: 178 | var value = /** @type {string} */ (reader.readString()); 179 | msg.setIsp(value); 180 | break; 181 | default: 182 | reader.skipField(); 183 | break; 184 | } 185 | } 186 | return msg; 187 | }; 188 | 189 | 190 | /** 191 | * Serializes the message to binary data (in protobuf wire format). 192 | * @return {!Uint8Array} 193 | */ 194 | proto.pInfo.prototype.serializeBinary = function() { 195 | var writer = new jspb.BinaryWriter(); 196 | proto.pInfo.serializeBinaryToWriter(this, writer); 197 | return writer.getResultBuffer(); 198 | }; 199 | 200 | 201 | /** 202 | * Serializes the given message to binary data (in protobuf wire 203 | * format), writing to the given BinaryWriter. 204 | * @param {!proto.pInfo} message 205 | * @param {!jspb.BinaryWriter} writer 206 | * @suppress {unusedLocalVariables} f is only used for nested messages 207 | */ 208 | proto.pInfo.serializeBinaryToWriter = function(message, writer) { 209 | var f = undefined; 210 | f = message.getCityId(); 211 | if (f !== 0) { 212 | writer.writeInt32( 213 | 1, 214 | f 215 | ); 216 | } 217 | f = message.getCountry(); 218 | if (f.length > 0) { 219 | writer.writeString( 220 | 2, 221 | f 222 | ); 223 | } 224 | f = message.getRegion(); 225 | if (f.length > 0) { 226 | writer.writeString( 227 | 3, 228 | f 229 | ); 230 | } 231 | f = message.getProvince(); 232 | if (f.length > 0) { 233 | writer.writeString( 234 | 4, 235 | f 236 | ); 237 | } 238 | f = message.getCity(); 239 | if (f.length > 0) { 240 | writer.writeString( 241 | 5, 242 | f 243 | ); 244 | } 245 | f = message.getIsp(); 246 | if (f.length > 0) { 247 | writer.writeString( 248 | 6, 249 | f 250 | ); 251 | } 252 | }; 253 | 254 | 255 | /** 256 | * optional int32 city_id = 1; 257 | * @return {number} 258 | */ 259 | proto.pInfo.prototype.getCityId = function() { 260 | return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); 261 | }; 262 | 263 | 264 | /** 265 | * @param {number} value 266 | * @return {!proto.pInfo} returns this 267 | */ 268 | proto.pInfo.prototype.setCityId = function(value) { 269 | return jspb.Message.setProto3IntField(this, 1, value); 270 | }; 271 | 272 | 273 | /** 274 | * optional string country = 2; 275 | * @return {string} 276 | */ 277 | proto.pInfo.prototype.getCountry = function() { 278 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); 279 | }; 280 | 281 | 282 | /** 283 | * @param {string} value 284 | * @return {!proto.pInfo} returns this 285 | */ 286 | proto.pInfo.prototype.setCountry = function(value) { 287 | return jspb.Message.setProto3StringField(this, 2, value); 288 | }; 289 | 290 | 291 | /** 292 | * optional string region = 3; 293 | * @return {string} 294 | */ 295 | proto.pInfo.prototype.getRegion = function() { 296 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); 297 | }; 298 | 299 | 300 | /** 301 | * @param {string} value 302 | * @return {!proto.pInfo} returns this 303 | */ 304 | proto.pInfo.prototype.setRegion = function(value) { 305 | return jspb.Message.setProto3StringField(this, 3, value); 306 | }; 307 | 308 | 309 | /** 310 | * optional string province = 4; 311 | * @return {string} 312 | */ 313 | proto.pInfo.prototype.getProvince = function() { 314 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); 315 | }; 316 | 317 | 318 | /** 319 | * @param {string} value 320 | * @return {!proto.pInfo} returns this 321 | */ 322 | proto.pInfo.prototype.setProvince = function(value) { 323 | return jspb.Message.setProto3StringField(this, 4, value); 324 | }; 325 | 326 | 327 | /** 328 | * optional string city = 5; 329 | * @return {string} 330 | */ 331 | proto.pInfo.prototype.getCity = function() { 332 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); 333 | }; 334 | 335 | 336 | /** 337 | * @param {string} value 338 | * @return {!proto.pInfo} returns this 339 | */ 340 | proto.pInfo.prototype.setCity = function(value) { 341 | return jspb.Message.setProto3StringField(this, 5, value); 342 | }; 343 | 344 | 345 | /** 346 | * optional string isp = 6; 347 | * @return {string} 348 | */ 349 | proto.pInfo.prototype.getIsp = function() { 350 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); 351 | }; 352 | 353 | 354 | /** 355 | * @param {string} value 356 | * @return {!proto.pInfo} returns this 357 | */ 358 | proto.pInfo.prototype.setIsp = function(value) { 359 | return jspb.Message.setProto3StringField(this, 6, value); 360 | }; 361 | 362 | 363 | 364 | 365 | 366 | if (jspb.Message.GENERATE_TO_OBJECT) { 367 | /** 368 | * Creates an object representation of this proto. 369 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 370 | * Optional fields that are not set will be set to undefined. 371 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 372 | * For the list of reserved names please see: 373 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 374 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 375 | * JSPB instance for transitional soy proto support: 376 | * http://goto/soy-param-migration 377 | * @return {!Object} 378 | */ 379 | proto.botStatusRequest.prototype.toObject = function(opt_includeInstance) { 380 | return proto.botStatusRequest.toObject(opt_includeInstance, this); 381 | }; 382 | 383 | 384 | /** 385 | * Static version of the {@see toObject} method. 386 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 387 | * the JSPB instance for transitional soy proto support: 388 | * http://goto/soy-param-migration 389 | * @param {!proto.botStatusRequest} msg The msg instance to transform. 390 | * @return {!Object} 391 | * @suppress {unusedLocalVariables} f is only used for nested messages 392 | */ 393 | proto.botStatusRequest.toObject = function(includeInstance, msg) { 394 | var f, obj = { 395 | botId: jspb.Message.getFieldWithDefault(msg, 1, ""), 396 | x: jspb.Message.getFloatingPointFieldWithDefault(msg, 2, 0.0), 397 | y: jspb.Message.getFloatingPointFieldWithDefault(msg, 3, 0.0), 398 | eyeX: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0), 399 | eyeY: jspb.Message.getFloatingPointFieldWithDefault(msg, 5, 0.0), 400 | msg: jspb.Message.getFieldWithDefault(msg, 6, ""), 401 | realX: jspb.Message.getFloatingPointFieldWithDefault(msg, 7, 0.0), 402 | realY: jspb.Message.getFloatingPointFieldWithDefault(msg, 8, 0.0), 403 | status: jspb.Message.getFieldWithDefault(msg, 9, 0), 404 | name: jspb.Message.getFieldWithDefault(msg, 10, ""), 405 | gender: jspb.Message.getFieldWithDefault(msg, 11, 0), 406 | posInfo: (f = msg.getPosInfo()) && proto.pInfo.toObject(includeInstance, f) 407 | }; 408 | 409 | if (includeInstance) { 410 | obj.$jspbMessageInstance = msg; 411 | } 412 | return obj; 413 | }; 414 | } 415 | 416 | 417 | /** 418 | * Deserializes binary data (in protobuf wire format). 419 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 420 | * @return {!proto.botStatusRequest} 421 | */ 422 | proto.botStatusRequest.deserializeBinary = function(bytes) { 423 | var reader = new jspb.BinaryReader(bytes); 424 | var msg = new proto.botStatusRequest; 425 | return proto.botStatusRequest.deserializeBinaryFromReader(msg, reader); 426 | }; 427 | 428 | 429 | /** 430 | * Deserializes binary data (in protobuf wire format) from the 431 | * given reader into the given message object. 432 | * @param {!proto.botStatusRequest} msg The message object to deserialize into. 433 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 434 | * @return {!proto.botStatusRequest} 435 | */ 436 | proto.botStatusRequest.deserializeBinaryFromReader = function(msg, reader) { 437 | while (reader.nextField()) { 438 | if (reader.isEndGroup()) { 439 | break; 440 | } 441 | var field = reader.getFieldNumber(); 442 | switch (field) { 443 | case 1: 444 | var value = /** @type {string} */ (reader.readString()); 445 | msg.setBotId(value); 446 | break; 447 | case 2: 448 | var value = /** @type {number} */ (reader.readFloat()); 449 | msg.setX(value); 450 | break; 451 | case 3: 452 | var value = /** @type {number} */ (reader.readFloat()); 453 | msg.setY(value); 454 | break; 455 | case 4: 456 | var value = /** @type {number} */ (reader.readFloat()); 457 | msg.setEyeX(value); 458 | break; 459 | case 5: 460 | var value = /** @type {number} */ (reader.readFloat()); 461 | msg.setEyeY(value); 462 | break; 463 | case 6: 464 | var value = /** @type {string} */ (reader.readString()); 465 | msg.setMsg(value); 466 | break; 467 | case 7: 468 | var value = /** @type {number} */ (reader.readFloat()); 469 | msg.setRealX(value); 470 | break; 471 | case 8: 472 | var value = /** @type {number} */ (reader.readFloat()); 473 | msg.setRealY(value); 474 | break; 475 | case 9: 476 | var value = /** @type {!proto.botStatusRequest.status_type} */ (reader.readEnum()); 477 | msg.setStatus(value); 478 | break; 479 | case 10: 480 | var value = /** @type {string} */ (reader.readString()); 481 | msg.setName(value); 482 | break; 483 | case 11: 484 | var value = /** @type {!proto.botStatusRequest.gender_type} */ (reader.readEnum()); 485 | msg.setGender(value); 486 | break; 487 | case 12: 488 | var value = new proto.pInfo; 489 | reader.readMessage(value,proto.pInfo.deserializeBinaryFromReader); 490 | msg.setPosInfo(value); 491 | break; 492 | default: 493 | reader.skipField(); 494 | break; 495 | } 496 | } 497 | return msg; 498 | }; 499 | 500 | 501 | /** 502 | * Serializes the message to binary data (in protobuf wire format). 503 | * @return {!Uint8Array} 504 | */ 505 | proto.botStatusRequest.prototype.serializeBinary = function() { 506 | var writer = new jspb.BinaryWriter(); 507 | proto.botStatusRequest.serializeBinaryToWriter(this, writer); 508 | return writer.getResultBuffer(); 509 | }; 510 | 511 | 512 | /** 513 | * Serializes the given message to binary data (in protobuf wire 514 | * format), writing to the given BinaryWriter. 515 | * @param {!proto.botStatusRequest} message 516 | * @param {!jspb.BinaryWriter} writer 517 | * @suppress {unusedLocalVariables} f is only used for nested messages 518 | */ 519 | proto.botStatusRequest.serializeBinaryToWriter = function(message, writer) { 520 | var f = undefined; 521 | f = message.getBotId(); 522 | if (f.length > 0) { 523 | writer.writeString( 524 | 1, 525 | f 526 | ); 527 | } 528 | f = message.getX(); 529 | if (f !== 0.0) { 530 | writer.writeFloat( 531 | 2, 532 | f 533 | ); 534 | } 535 | f = message.getY(); 536 | if (f !== 0.0) { 537 | writer.writeFloat( 538 | 3, 539 | f 540 | ); 541 | } 542 | f = message.getEyeX(); 543 | if (f !== 0.0) { 544 | writer.writeFloat( 545 | 4, 546 | f 547 | ); 548 | } 549 | f = message.getEyeY(); 550 | if (f !== 0.0) { 551 | writer.writeFloat( 552 | 5, 553 | f 554 | ); 555 | } 556 | f = message.getMsg(); 557 | if (f.length > 0) { 558 | writer.writeString( 559 | 6, 560 | f 561 | ); 562 | } 563 | f = message.getRealX(); 564 | if (f !== 0.0) { 565 | writer.writeFloat( 566 | 7, 567 | f 568 | ); 569 | } 570 | f = message.getRealY(); 571 | if (f !== 0.0) { 572 | writer.writeFloat( 573 | 8, 574 | f 575 | ); 576 | } 577 | f = message.getStatus(); 578 | if (f !== 0.0) { 579 | writer.writeEnum( 580 | 9, 581 | f 582 | ); 583 | } 584 | f = message.getName(); 585 | if (f.length > 0) { 586 | writer.writeString( 587 | 10, 588 | f 589 | ); 590 | } 591 | f = message.getGender(); 592 | if (f !== 0.0) { 593 | writer.writeEnum( 594 | 11, 595 | f 596 | ); 597 | } 598 | f = message.getPosInfo(); 599 | if (f != null) { 600 | writer.writeMessage( 601 | 12, 602 | f, 603 | proto.pInfo.serializeBinaryToWriter 604 | ); 605 | } 606 | }; 607 | 608 | 609 | /** 610 | * @enum {number} 611 | */ 612 | proto.botStatusRequest.status_type = { 613 | WAITING: 0, 614 | CONNECTING: 1, 615 | CLOSE: 2 616 | }; 617 | 618 | /** 619 | * @enum {number} 620 | */ 621 | proto.botStatusRequest.gender_type = { 622 | MAN: 0, 623 | WOMAN: 1 624 | }; 625 | 626 | /** 627 | * optional string bot_id = 1; 628 | * @return {string} 629 | */ 630 | proto.botStatusRequest.prototype.getBotId = function() { 631 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 632 | }; 633 | 634 | 635 | /** 636 | * @param {string} value 637 | * @return {!proto.botStatusRequest} returns this 638 | */ 639 | proto.botStatusRequest.prototype.setBotId = function(value) { 640 | return jspb.Message.setProto3StringField(this, 1, value); 641 | }; 642 | 643 | 644 | /** 645 | * optional float x = 2; 646 | * @return {number} 647 | */ 648 | proto.botStatusRequest.prototype.getX = function() { 649 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 2, 0.0)); 650 | }; 651 | 652 | 653 | /** 654 | * @param {number} value 655 | * @return {!proto.botStatusRequest} returns this 656 | */ 657 | proto.botStatusRequest.prototype.setX = function(value) { 658 | return jspb.Message.setProto3FloatField(this, 2, value); 659 | }; 660 | 661 | 662 | /** 663 | * optional float y = 3; 664 | * @return {number} 665 | */ 666 | proto.botStatusRequest.prototype.getY = function() { 667 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 3, 0.0)); 668 | }; 669 | 670 | 671 | /** 672 | * @param {number} value 673 | * @return {!proto.botStatusRequest} returns this 674 | */ 675 | proto.botStatusRequest.prototype.setY = function(value) { 676 | return jspb.Message.setProto3FloatField(this, 3, value); 677 | }; 678 | 679 | 680 | /** 681 | * optional float eye_x = 4; 682 | * @return {number} 683 | */ 684 | proto.botStatusRequest.prototype.getEyeX = function() { 685 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 4, 0.0)); 686 | }; 687 | 688 | 689 | /** 690 | * @param {number} value 691 | * @return {!proto.botStatusRequest} returns this 692 | */ 693 | proto.botStatusRequest.prototype.setEyeX = function(value) { 694 | return jspb.Message.setProto3FloatField(this, 4, value); 695 | }; 696 | 697 | 698 | /** 699 | * optional float eye_y = 5; 700 | * @return {number} 701 | */ 702 | proto.botStatusRequest.prototype.getEyeY = function() { 703 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 5, 0.0)); 704 | }; 705 | 706 | 707 | /** 708 | * @param {number} value 709 | * @return {!proto.botStatusRequest} returns this 710 | */ 711 | proto.botStatusRequest.prototype.setEyeY = function(value) { 712 | return jspb.Message.setProto3FloatField(this, 5, value); 713 | }; 714 | 715 | 716 | /** 717 | * optional string msg = 6; 718 | * @return {string} 719 | */ 720 | proto.botStatusRequest.prototype.getMsg = function() { 721 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); 722 | }; 723 | 724 | 725 | /** 726 | * @param {string} value 727 | * @return {!proto.botStatusRequest} returns this 728 | */ 729 | proto.botStatusRequest.prototype.setMsg = function(value) { 730 | return jspb.Message.setProto3StringField(this, 6, value); 731 | }; 732 | 733 | 734 | /** 735 | * optional float real_x = 7; 736 | * @return {number} 737 | */ 738 | proto.botStatusRequest.prototype.getRealX = function() { 739 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 7, 0.0)); 740 | }; 741 | 742 | 743 | /** 744 | * @param {number} value 745 | * @return {!proto.botStatusRequest} returns this 746 | */ 747 | proto.botStatusRequest.prototype.setRealX = function(value) { 748 | return jspb.Message.setProto3FloatField(this, 7, value); 749 | }; 750 | 751 | 752 | /** 753 | * optional float real_y = 8; 754 | * @return {number} 755 | */ 756 | proto.botStatusRequest.prototype.getRealY = function() { 757 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 8, 0.0)); 758 | }; 759 | 760 | 761 | /** 762 | * @param {number} value 763 | * @return {!proto.botStatusRequest} returns this 764 | */ 765 | proto.botStatusRequest.prototype.setRealY = function(value) { 766 | return jspb.Message.setProto3FloatField(this, 8, value); 767 | }; 768 | 769 | 770 | /** 771 | * optional status_type status = 9; 772 | * @return {!proto.botStatusRequest.status_type} 773 | */ 774 | proto.botStatusRequest.prototype.getStatus = function() { 775 | return /** @type {!proto.botStatusRequest.status_type} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); 776 | }; 777 | 778 | 779 | /** 780 | * @param {!proto.botStatusRequest.status_type} value 781 | * @return {!proto.botStatusRequest} returns this 782 | */ 783 | proto.botStatusRequest.prototype.setStatus = function(value) { 784 | return jspb.Message.setProto3EnumField(this, 9, value); 785 | }; 786 | 787 | 788 | /** 789 | * optional string name = 10; 790 | * @return {string} 791 | */ 792 | proto.botStatusRequest.prototype.getName = function() { 793 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, "")); 794 | }; 795 | 796 | 797 | /** 798 | * @param {string} value 799 | * @return {!proto.botStatusRequest} returns this 800 | */ 801 | proto.botStatusRequest.prototype.setName = function(value) { 802 | return jspb.Message.setProto3StringField(this, 10, value); 803 | }; 804 | 805 | 806 | /** 807 | * optional gender_type gender = 11; 808 | * @return {!proto.botStatusRequest.gender_type} 809 | */ 810 | proto.botStatusRequest.prototype.getGender = function() { 811 | return /** @type {!proto.botStatusRequest.gender_type} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); 812 | }; 813 | 814 | 815 | /** 816 | * @param {!proto.botStatusRequest.gender_type} value 817 | * @return {!proto.botStatusRequest} returns this 818 | */ 819 | proto.botStatusRequest.prototype.setGender = function(value) { 820 | return jspb.Message.setProto3EnumField(this, 11, value); 821 | }; 822 | 823 | 824 | /** 825 | * optional pInfo pos_info = 12; 826 | * @return {?proto.pInfo} 827 | */ 828 | proto.botStatusRequest.prototype.getPosInfo = function() { 829 | return /** @type{?proto.pInfo} */ ( 830 | jspb.Message.getWrapperField(this, proto.pInfo, 12)); 831 | }; 832 | 833 | 834 | /** 835 | * @param {?proto.pInfo|undefined} value 836 | * @return {!proto.botStatusRequest} returns this 837 | */ 838 | proto.botStatusRequest.prototype.setPosInfo = function(value) { 839 | return jspb.Message.setWrapperField(this, 12, value); 840 | }; 841 | 842 | 843 | /** 844 | * Clears the message field making it undefined. 845 | * @return {!proto.botStatusRequest} returns this 846 | */ 847 | proto.botStatusRequest.prototype.clearPosInfo = function() { 848 | return this.setPosInfo(undefined); 849 | }; 850 | 851 | 852 | /** 853 | * Returns whether this field is set. 854 | * @return {boolean} 855 | */ 856 | proto.botStatusRequest.prototype.hasPosInfo = function() { 857 | return jspb.Message.getField(this, 12) != null; 858 | }; 859 | 860 | 861 | 862 | /** 863 | * List of repeated fields within this message type. 864 | * @private {!Array} 865 | * @const 866 | */ 867 | proto.botStatusResponse.repeatedFields_ = [1]; 868 | 869 | 870 | 871 | if (jspb.Message.GENERATE_TO_OBJECT) { 872 | /** 873 | * Creates an object representation of this proto. 874 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 875 | * Optional fields that are not set will be set to undefined. 876 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 877 | * For the list of reserved names please see: 878 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 879 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 880 | * JSPB instance for transitional soy proto support: 881 | * http://goto/soy-param-migration 882 | * @return {!Object} 883 | */ 884 | proto.botStatusResponse.prototype.toObject = function(opt_includeInstance) { 885 | return proto.botStatusResponse.toObject(opt_includeInstance, this); 886 | }; 887 | 888 | 889 | /** 890 | * Static version of the {@see toObject} method. 891 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 892 | * the JSPB instance for transitional soy proto support: 893 | * http://goto/soy-param-migration 894 | * @param {!proto.botStatusResponse} msg The msg instance to transform. 895 | * @return {!Object} 896 | * @suppress {unusedLocalVariables} f is only used for nested messages 897 | */ 898 | proto.botStatusResponse.toObject = function(includeInstance, msg) { 899 | var f, obj = { 900 | botStatusList: jspb.Message.toObjectList(msg.getBotStatusList(), 901 | proto.botStatusRequest.toObject, includeInstance) 902 | }; 903 | 904 | if (includeInstance) { 905 | obj.$jspbMessageInstance = msg; 906 | } 907 | return obj; 908 | }; 909 | } 910 | 911 | 912 | /** 913 | * Deserializes binary data (in protobuf wire format). 914 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 915 | * @return {!proto.botStatusResponse} 916 | */ 917 | proto.botStatusResponse.deserializeBinary = function(bytes) { 918 | var reader = new jspb.BinaryReader(bytes); 919 | var msg = new proto.botStatusResponse; 920 | return proto.botStatusResponse.deserializeBinaryFromReader(msg, reader); 921 | }; 922 | 923 | 924 | /** 925 | * Deserializes binary data (in protobuf wire format) from the 926 | * given reader into the given message object. 927 | * @param {!proto.botStatusResponse} msg The message object to deserialize into. 928 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 929 | * @return {!proto.botStatusResponse} 930 | */ 931 | proto.botStatusResponse.deserializeBinaryFromReader = function(msg, reader) { 932 | while (reader.nextField()) { 933 | if (reader.isEndGroup()) { 934 | break; 935 | } 936 | var field = reader.getFieldNumber(); 937 | switch (field) { 938 | case 1: 939 | var value = new proto.botStatusRequest; 940 | reader.readMessage(value,proto.botStatusRequest.deserializeBinaryFromReader); 941 | msg.addBotStatus(value); 942 | break; 943 | default: 944 | reader.skipField(); 945 | break; 946 | } 947 | } 948 | return msg; 949 | }; 950 | 951 | 952 | /** 953 | * Serializes the message to binary data (in protobuf wire format). 954 | * @return {!Uint8Array} 955 | */ 956 | proto.botStatusResponse.prototype.serializeBinary = function() { 957 | var writer = new jspb.BinaryWriter(); 958 | proto.botStatusResponse.serializeBinaryToWriter(this, writer); 959 | return writer.getResultBuffer(); 960 | }; 961 | 962 | 963 | /** 964 | * Serializes the given message to binary data (in protobuf wire 965 | * format), writing to the given BinaryWriter. 966 | * @param {!proto.botStatusResponse} message 967 | * @param {!jspb.BinaryWriter} writer 968 | * @suppress {unusedLocalVariables} f is only used for nested messages 969 | */ 970 | proto.botStatusResponse.serializeBinaryToWriter = function(message, writer) { 971 | var f = undefined; 972 | f = message.getBotStatusList(); 973 | if (f.length > 0) { 974 | writer.writeRepeatedMessage( 975 | 1, 976 | f, 977 | proto.botStatusRequest.serializeBinaryToWriter 978 | ); 979 | } 980 | }; 981 | 982 | 983 | /** 984 | * repeated botStatusRequest bot_status = 1; 985 | * @return {!Array} 986 | */ 987 | proto.botStatusResponse.prototype.getBotStatusList = function() { 988 | return /** @type{!Array} */ ( 989 | jspb.Message.getRepeatedWrapperField(this, proto.botStatusRequest, 1)); 990 | }; 991 | 992 | 993 | /** 994 | * @param {!Array} value 995 | * @return {!proto.botStatusResponse} returns this 996 | */ 997 | proto.botStatusResponse.prototype.setBotStatusList = function(value) { 998 | return jspb.Message.setRepeatedWrapperField(this, 1, value); 999 | }; 1000 | 1001 | 1002 | /** 1003 | * @param {!proto.botStatusRequest=} opt_value 1004 | * @param {number=} opt_index 1005 | * @return {!proto.botStatusRequest} 1006 | */ 1007 | proto.botStatusResponse.prototype.addBotStatus = function(opt_value, opt_index) { 1008 | return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.botStatusRequest, opt_index); 1009 | }; 1010 | 1011 | 1012 | /** 1013 | * Clears the list making it empty but non-null. 1014 | * @return {!proto.botStatusResponse} returns this 1015 | */ 1016 | proto.botStatusResponse.prototype.clearBotStatusList = function() { 1017 | return this.setBotStatusList([]); 1018 | }; 1019 | 1020 | 1021 | goog.object.extend(exports, proto); 1022 | -------------------------------------------------------------------------------- /supervisor/go-space-chat.ini: -------------------------------------------------------------------------------- 1 | [program:go-space-chat] 2 | directory=/www/go-space-chat/ 3 | command= go run main.go 4 | 5 | startsecs=1 ; 也是一样,进程启动后跑了几秒钟,才被认定为成功启动,默认1 6 | startretries=5 ; 失败最大尝试次数,默认3 7 | 8 | redirect_stderr=true 9 | stdout_logfile=AUTO 10 | -------------------------------------------------------------------------------- /web_resource/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | *.sw? 21 | -------------------------------------------------------------------------------- /web_resource/README.md: -------------------------------------------------------------------------------- 1 | # app 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | yarn lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /web_resource/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /web_resource/dist/css/app.4c726756.css: -------------------------------------------------------------------------------- 1 | #app,body{margin:0;padding:0}body{overflow:hidden;position:fixed;top:0;left:0;font-size:12px} -------------------------------------------------------------------------------- /web_resource/dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/dist/favicon.ico -------------------------------------------------------------------------------- /web_resource/dist/image/human.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/dist/image/human.png -------------------------------------------------------------------------------- /web_resource/dist/image/m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/dist/image/m.png -------------------------------------------------------------------------------- /web_resource/dist/image/w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/dist/image/w.png -------------------------------------------------------------------------------- /web_resource/dist/index.html: -------------------------------------------------------------------------------- 1 | app
-------------------------------------------------------------------------------- /web_resource/dist/js/app.26d1b8ef.js: -------------------------------------------------------------------------------- 1 | (function(t){function e(e){for(var r,a,i=e[0],u=e[1],l=e[2],p=0,c=[];p1&&void 0!==arguments[1]?arguments[1]:[0,0,0];for(var o in t){var r=t[o];r.x+=e[0],r.y+=e[1],r.z+=e[2],r.x>l.width?r.x-=l.width:r.x<0&&(r.x+=l.width),r.y>l.height?r.y-=l.height:r.y<0&&(r.y+=l.height),t[o]=r}return t}function G(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return Math.floor(Math.random()*(t-e))+e}function X(t){var e=Math.floor(155*t/(h.z.max-h.z.min))+100;return"rgb("+e+","+e+","+e+")"}function Y(t){return t*f/c.z}function $(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];for(var o in e){var r=H(e[o].x,e[o].y,e[o].z,e[o].c,e[o].s);(r.x0&&void 0!==arguments[0]?arguments[0]:l.width/2,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:l.height/2;u.beginPath(),t+=5,e+=5,u.arc(t,e,8,0,2*Math.PI),z.gender===proto.botStatusRequest.gender_type.WOMAN?u.fillStyle="rgb(255,20,147)":u.fillStyle="rgb(0,191,255)",u.fill(),u.beginPath();var o=K(t,e,8);z.e_x=o[0],z.e_y=o[1],u.arc(o[0],o[1],4,0,2*Math.PI),u.fillStyle="rgb(255,255,255)",u.fill(),u.font="14px Arial",z.gender===proto.botStatusRequest.gender_type.WOMAN?u.fillStyle="rgb(255,20,147)":u.fillStyle="rgb(0,191,255)",u.fillText(z.name,t-8,e+20)}function V(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:l.width/2,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:l.height/2,o=arguments.length>2?arguments[2]:void 0,r=arguments.length>3?arguments[3]:void 0,n=arguments.length>4?arguments[4]:void 0,s=arguments.length>5?arguments[5]:void 0,a=arguments.length>6?arguments[6]:void 0;u.beginPath(),t+=5,e+=5,u.arc(t,e,8,0,2*Math.PI),s===proto.botStatusRequest.gender_type.WOMAN?u.fillStyle="rgb(255,20,147)":u.fillStyle="rgb(0,191,255)",u.fill(),u.beginPath(),u.arc(o,r,4,0,2*Math.PI),u.fillStyle="rgb(255,255,255)",u.fill(),u.font="14px Arial",s===proto.botStatusRequest.gender_type.WOMAN?u.fillStyle="rgb(255,20,147)":u.fillStyle="rgb(0,191,255)",u.fillText(n,t-8,e+20),u.fillText(a,t-8,e+40)}function K(t,e,o){var r=5,n=(t-M.x)*(o-r)/Math.sqrt(Math.pow(t-M.x,2)+Math.pow(e-M.y,2)),s=(e-M.y)*(o-r)/Math.sqrt(Math.pow(t-M.x,2)+Math.pow(e-M.y,2));m.up?(n=0,s=o-r):m.down&&(n=0,s=-(o-r)),m.left?(n=o-r,s=0):m.right&&(n=-(o-r),s=0);var a=Math.sqrt(Math.pow(o-r,2)/2);return m.up&&m.left?(n=a,s=a):m.up&&m.right?(n=-a,s=a):m.down&&m.left?(n=a,s=-a):m.down&&m.right&&(n=-a,s=-a),[t-n,e-s]}function Q(){window.addEventListener("keydown",(function(t){if(F&&t.keyCode!=g.send)return!1;switch(t.keyCode){case g.up:m.up=!0,m.down=!1;break;case g.down:m.up=!1,m.down=!0;break;case g.right:m.right=!0,m.left=!1;break;case g.left:m.left=!0,m.right=!1;break;case g.talk:Z();break;case g.send:tt();break}})),window.addEventListener("keyup",(function(t){switch(t.keyCode){case g.up:m.up=!1;break;case g.down:m.down=!1;break;case g.right:m.right=!1;break;case g.left:m.left=!1;break}t.keyCode!=g.up&&t.keyCode!=g.down&&t.keyCode!=g.left&&t.keyCode!=g.right||(j=!0,v=1)})),window.addEventListener("mousemove",(function(t){M={x:t.x,y:t.y}})),document.body.addEventListener("touchmove",(function(t){t.preventDefault()}))}function Z(){if(null!=R)return!1;F=!0,R=document.createElement("input"),R.setAttribute("style","position:fixed;left:"+c.x+"px;top:"+(c.y+30)+"px;background-color:rgba(200,200,200,0.2);border:1px solid rgba(200,200,200,0.2);border-radius:10px;padding:5px;outline:none;width:150px;color:white;font-size:12px"),R.setAttribute("maxlength",50),document.body.appendChild(R),R.addEventListener("focus",(function(){})),R.addEventListener("blur",(function(){document.body.removeChild(R),R=null,F=!1})),R.focus()}function tt(){if(!R||!R.value)return!1;var t=R.value;R.blur(),ft(t)}function et(t){var e=document.createElement("p");e.innerHTML=""+t+"",k.appendChild(e),setTimeout((function(){k.removeChild(e)}),8e3)}function ot(){for(var t in P)t===z.bot_id?(null==k&&(k=document.createElement("div"),k.setAttribute("style","position:fixed;left:"+c.x+"px;bottom:"+(l.height-c.y+20)+"px;color:white;font-size:12px"),document.body.appendChild(k)),P[t].msg&&(et(P[t].msg),P[t].msg="")):t!==z.bot_id&&rt(P[t].r_x+P[t].x-q.x,P[t].r_y+P[t].y-q.y)&&(V(P[t].r_x+P[t].x-q.x,P[t].r_y+P[t].y-q.y,P[t].r_x+P[t].e_x-q.x,P[t].r_y+P[t].e_y-q.y,P[t].name,P[t].gender,P[t].pos_info.getCity()),nt(t),P[t].msg&&(st(t,P[t].msg),P[t].msg=""),at(t))}function rt(t,e){return!(t<0)&&(!(e<0)&&(!(t>l.width)&&!(e>l.height)))}function nt(t){W[t]||(W[t]=document.createElement("div"),W[t].setAttribute("style","position:fixed;left:"+(P[t].x+P[t].r_x-q.x)+"px;bottom:"+(l.height-(P[t].y+P[t].r_y-q.y)+20)+"px;color:white;font-size:12px"),document.body.appendChild(W[t]))}function st(t,e){P[t];var o=W[t],r=document.createElement("p");r.innerHTML=""+e+"",o.appendChild(r),setTimeout((function(){o.removeChild(r)}),15e3)}function at(t){var e=P[t],o=W[t];o.setAttribute("style","position:fixed;left:"+(e.x+e.r_x-q.x)+"px;bottom:"+(l.height-(e.y+e.r_y-q.y)+20)+"px;color:white;font-size:12px")}function it(){null!=k&&k.setAttribute("style","position:fixed;left:"+c.x+"px;bottom:"+(l.height-c.y+20)+"px;color:white;font-size:12px")}function ut(){var t=b.y,e=b.x,o=0,r=0;if(m.up?(t=b.y-y,r=-y):m.down&&(t=b.y+y,r=y),m.left?(e=b.x-y,o=-y):m.right&&(e=b.x+y,o=y),o||r){lt("far");var n=100;dt(e,t,l.width/2,l.height/2)>=n?(j=!1,v=0,x=[-o,-r,0],q.x+=o,q.y+=r,z.r_x=q.x,z.r_y=q.y):(b.y=t,b.x=e)}else lt("near");c.x=b.x,c.y=b.y,it(),U(b.x,b.y)}function lt(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"far",e=.1,o=5,r=1;"far"==t?S0&&(S-=e,c.z=c.z-r*Math.sin(S)/o)}function dt(t,e,o,r){return Math.sqrt(Math.pow(t-o,2)+Math.pow(e-r,2))}function pt(){I=new WebSocket("ws://"+location.hostname+":9000/ws"),I.binaryType="arraybuffer",I.onopen=function(){console.info("ws open"),E=!0},I.onmessage=function(t){var e=proto.botStatusResponse.deserializeBinary(t.data),o=e.getBotStatusList();for(var r in o)o[r].getStatus()!==proto.botStatusRequest.status_type.CLOSE?(P[o[r].getBotId()]={x:o[r].getX(),y:o[r].getY(),e_x:o[r].getEyeX(),e_y:o[r].getEyeY(),r_x:o[r].getRealX(),r_y:o[r].getRealY(),msg:o[r].getMsg(),name:o[r].getName(),gender:o[r].getGender(),pos_info:o[r].getPosInfo()},xt(P[o[r].getBotId()])):delete P[o[r].getBotId()]},I.onclose=function(){console.info("ws close")}}var ct=!0;function ft(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";if(!E)return!1;var e=!1;if(_)for(var o in z)z[o]!==_[o]&&(e=!0);else e=!0;if(""===t){if(!ct)return;ct=!1,setTimeout((function(){ct=!0}),30)}if(e||t){var r=new proto.botStatusRequest;r.setBotId(z.bot_id),r.setX(c.x),r.setY(c.y),r.setEyeX(z.e_x),r.setEyeY(z.e_y),r.setRealX(q.x),r.setRealY(q.y),r.setMsg(t),r.setName(z.name),r.setGender(z.gender),I.send(r.serializeBinary()),Object.assign(_,z)}}function gt(){var t=localStorage.getItem("star_name");z.name=null!==t&&""!==t?t:"Guest"+Math.random().toString(36).substr(2);var e=localStorage.getItem("star_gender");z.gender=null!==e?parseInt(e):proto.botStatusRequest.gender_type.MAN}function bt(){var t=document.createElement("div");t.setAttribute("style","position:fixed;text-align:center;left:5px;top:50px;width:30px;height:200px;background-color:rgba(0,0,0,0.5);border:1px solid rgba(0,0,0,0.5);border-radius:5px;"),document.body.appendChild(t);var e=ht(t,"image/human.png","点我修改昵称"),o=null;e.addEventListener("click",(function(t){if(o)return!1;o=document.createElement("input"),o.setAttribute("style","position:fixed;left:50px;top:50px;background-color:white;border:1px solid white;border-radius:5px;padding:5px;outline:none;width:150px;font-size:12px"),o.setAttribute("placeholder","请输入昵称,长度10"),o.setAttribute("maxlength",10),document.body.appendChild(o),o.focus(),o.addEventListener("blur",(function(){""!==o.value&&(z.name=o.value,localStorage.setItem("star_name",z.name)),document.body.removeChild(o),o=null}))}));var r=ht(t,"image/m.png","男生");r.addEventListener("click",(function(t){z.gender=proto.botStatusRequest.gender_type.MAN,localStorage.setItem("star_gender",z.gender)}));var n=ht(t,"image/w.png","女生");n.addEventListener("click",(function(t){z.gender=proto.botStatusRequest.gender_type.WOMAN,localStorage.setItem("star_gender",z.gender)}))}function ht(t,e){var o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",r=document.createElement("img");return r.setAttribute("style","width:25px;height:25px;border:1px solid rgba(200,200,0,0.5);color:white;cursor:default;border-radius:5px;"),r.setAttribute("src",e),r.setAttribute("title",o),t.appendChild(r),r}function yt(){B.setAttribute("style","position:fixed;right:5px;bottom:200px;width:400px;height:70%;color:rgba(200,200,200,0.8);border:1px solid rgba(200,200,200,0.8);cursor:default;overflow-y:auto;border-radius:5px;"),document.body.appendChild(B)}function mt(t,e){if(""!==e.trim()){var o=document.createElement("div");o.setAttribute("style","margin:2px;"),o.innerHTML="
"+t+":"+e,B.appendChild(o),B.scrollTop=B.scrollHeight}}function xt(t){if(""!==t.msg.trim()){var e=document.createElement("div");e.setAttribute("style","margin:2px;"),e.innerHTML="
["+t.pos_info.getCity()+t.pos_info.getIsp()+"]@"+t.name+":"+t.msg,B.appendChild(e),B.scrollTop=B.scrollHeight}}function jt(){var t=["欢迎来到这个秘密的地方,茫茫人海,如果能在这里相遇,说明是一种缘分~","互动方式如下:","1. W A S D进行上下左右","2. 空格开启聊天框,回车发送消息","3. 左上角修改昵称、性别,点击空白修改成功","4. 新增用户上线频率全天分布图","git 地址:https://github.com/sunshinev/go-space-chat","前端 Vue+canvas+websocket+protobuf,后端 Golang+websocket+protobuf+goroutine"];for(var e in t)mt("管理员",t[e])}var vt=function(){yt(),jt(),O(),Q(),bt(),gt(),pt(),window.requestAnimationFrame(D)},St={data:function(){return{}},methods:{},mounted:function(){vt()}},wt=St,Mt=o("2877"),Rt=Object(Mt["a"])(wt,a,i,!1,null,null,null),Ft=Rt.exports,kt=function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)},It=[function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("div",{staticStyle:{width:"100%",height:"256px",position:"absolute",bottom:"0","z-index":"10"},attrs:{height:"200"}},[o("canvas",{attrs:{id:"myChart"}})])}],zt=o("30ef"),_t=o.n(zt),Et=o("bc3a"),qt=o.n(Et),Pt={data:function(){return{xlist:[],ylist:[]}},methods:{getChartData:function(){var t=this;qt.a.get("/login_charts").then((function(e){t.xlist=e.data.x,t.ylist=e.data.y,t.renderCharts()}))},renderCharts:function(){var t=document.getElementById("myChart"),e={maintainAspectRatio:!1,spanGaps:!1,elements:{line:{tension:.4}},plugins:{filler:{propagate:!1}},scales:{xAxes:[{ticks:{autoSkip:!0,maxRotation:0,display:!0}}]}};new _t.a(t,{type:"line",data:{labels:this.xlist,datasets:[{backgroundColor:"rgba(255, 99, 132, 0.5)",borderColor:"rgba(255, 99, 132, 0.5)",data:this.ylist,label:"",fill:"start"}]},options:_t.a.helpers.merge(e,{title:{text:"用户上线全天时间分布图",display:!0,position:"bottom"}})})}},beforeMount:function(){},mounted:function(){this.getChartData()}},Wt=Pt,Bt=Object(Mt["a"])(Wt,kt,It,!1,null,null,null),Ct=Bt.exports,Ot={name:"App",components:{Donghua:Ft,LoginChart:Ct}},Dt=Ot,At=(o("034f"),Object(Mt["a"])(Dt,n,s,!1,null,null,null)),Tt=At.exports;new r["a"]({render:function(t){return t(Tt)}}).$mount("#app")},"85ec":function(t,e,o){},"9d58":function(t,e,o){var r=o("8513"),n=r,s=Function("return this")();n.exportSymbol("proto.botStatusRequest",null,s),n.exportSymbol("proto.botStatusRequest.gender_type",null,s),n.exportSymbol("proto.botStatusRequest.status_type",null,s),n.exportSymbol("proto.botStatusResponse",null,s),n.exportSymbol("proto.pInfo",null,s),proto.pInfo=function(t){r.Message.initialize(this,t,0,-1,null,null)},n.inherits(proto.pInfo,r.Message),n.DEBUG&&!COMPILED&&(proto.pInfo.displayName="proto.pInfo"),proto.botStatusRequest=function(t){r.Message.initialize(this,t,0,-1,null,null)},n.inherits(proto.botStatusRequest,r.Message),n.DEBUG&&!COMPILED&&(proto.botStatusRequest.displayName="proto.botStatusRequest"),proto.botStatusResponse=function(t){r.Message.initialize(this,t,0,-1,proto.botStatusResponse.repeatedFields_,null)},n.inherits(proto.botStatusResponse,r.Message),n.DEBUG&&!COMPILED&&(proto.botStatusResponse.displayName="proto.botStatusResponse"),r.Message.GENERATE_TO_OBJECT&&(proto.pInfo.prototype.toObject=function(t){return proto.pInfo.toObject(t,this)},proto.pInfo.toObject=function(t,e){var o={cityId:r.Message.getFieldWithDefault(e,1,0),country:r.Message.getFieldWithDefault(e,2,""),region:r.Message.getFieldWithDefault(e,3,""),province:r.Message.getFieldWithDefault(e,4,""),city:r.Message.getFieldWithDefault(e,5,""),isp:r.Message.getFieldWithDefault(e,6,"")};return t&&(o.$jspbMessageInstance=e),o}),proto.pInfo.deserializeBinary=function(t){var e=new r.BinaryReader(t),o=new proto.pInfo;return proto.pInfo.deserializeBinaryFromReader(o,e)},proto.pInfo.deserializeBinaryFromReader=function(t,e){while(e.nextField()){if(e.isEndGroup())break;var o=e.getFieldNumber();switch(o){case 1:var r=e.readInt32();t.setCityId(r);break;case 2:r=e.readString();t.setCountry(r);break;case 3:r=e.readString();t.setRegion(r);break;case 4:r=e.readString();t.setProvince(r);break;case 5:r=e.readString();t.setCity(r);break;case 6:r=e.readString();t.setIsp(r);break;default:e.skipField();break}}return t},proto.pInfo.prototype.serializeBinary=function(){var t=new r.BinaryWriter;return proto.pInfo.serializeBinaryToWriter(this,t),t.getResultBuffer()},proto.pInfo.serializeBinaryToWriter=function(t,e){var o=void 0;o=t.getCityId(),0!==o&&e.writeInt32(1,o),o=t.getCountry(),o.length>0&&e.writeString(2,o),o=t.getRegion(),o.length>0&&e.writeString(3,o),o=t.getProvince(),o.length>0&&e.writeString(4,o),o=t.getCity(),o.length>0&&e.writeString(5,o),o=t.getIsp(),o.length>0&&e.writeString(6,o)},proto.pInfo.prototype.getCityId=function(){return r.Message.getFieldWithDefault(this,1,0)},proto.pInfo.prototype.setCityId=function(t){return r.Message.setProto3IntField(this,1,t)},proto.pInfo.prototype.getCountry=function(){return r.Message.getFieldWithDefault(this,2,"")},proto.pInfo.prototype.setCountry=function(t){return r.Message.setProto3StringField(this,2,t)},proto.pInfo.prototype.getRegion=function(){return r.Message.getFieldWithDefault(this,3,"")},proto.pInfo.prototype.setRegion=function(t){return r.Message.setProto3StringField(this,3,t)},proto.pInfo.prototype.getProvince=function(){return r.Message.getFieldWithDefault(this,4,"")},proto.pInfo.prototype.setProvince=function(t){return r.Message.setProto3StringField(this,4,t)},proto.pInfo.prototype.getCity=function(){return r.Message.getFieldWithDefault(this,5,"")},proto.pInfo.prototype.setCity=function(t){return r.Message.setProto3StringField(this,5,t)},proto.pInfo.prototype.getIsp=function(){return r.Message.getFieldWithDefault(this,6,"")},proto.pInfo.prototype.setIsp=function(t){return r.Message.setProto3StringField(this,6,t)},r.Message.GENERATE_TO_OBJECT&&(proto.botStatusRequest.prototype.toObject=function(t){return proto.botStatusRequest.toObject(t,this)},proto.botStatusRequest.toObject=function(t,e){var o,n={botId:r.Message.getFieldWithDefault(e,1,""),x:r.Message.getFloatingPointFieldWithDefault(e,2,0),y:r.Message.getFloatingPointFieldWithDefault(e,3,0),eyeX:r.Message.getFloatingPointFieldWithDefault(e,4,0),eyeY:r.Message.getFloatingPointFieldWithDefault(e,5,0),msg:r.Message.getFieldWithDefault(e,6,""),realX:r.Message.getFloatingPointFieldWithDefault(e,7,0),realY:r.Message.getFloatingPointFieldWithDefault(e,8,0),status:r.Message.getFieldWithDefault(e,9,0),name:r.Message.getFieldWithDefault(e,10,""),gender:r.Message.getFieldWithDefault(e,11,0),posInfo:(o=e.getPosInfo())&&proto.pInfo.toObject(t,o)};return t&&(n.$jspbMessageInstance=e),n}),proto.botStatusRequest.deserializeBinary=function(t){var e=new r.BinaryReader(t),o=new proto.botStatusRequest;return proto.botStatusRequest.deserializeBinaryFromReader(o,e)},proto.botStatusRequest.deserializeBinaryFromReader=function(t,e){while(e.nextField()){if(e.isEndGroup())break;var o=e.getFieldNumber();switch(o){case 1:var r=e.readString();t.setBotId(r);break;case 2:r=e.readFloat();t.setX(r);break;case 3:r=e.readFloat();t.setY(r);break;case 4:r=e.readFloat();t.setEyeX(r);break;case 5:r=e.readFloat();t.setEyeY(r);break;case 6:r=e.readString();t.setMsg(r);break;case 7:r=e.readFloat();t.setRealX(r);break;case 8:r=e.readFloat();t.setRealY(r);break;case 9:r=e.readEnum();t.setStatus(r);break;case 10:r=e.readString();t.setName(r);break;case 11:r=e.readEnum();t.setGender(r);break;case 12:r=new proto.pInfo;e.readMessage(r,proto.pInfo.deserializeBinaryFromReader),t.setPosInfo(r);break;default:e.skipField();break}}return t},proto.botStatusRequest.prototype.serializeBinary=function(){var t=new r.BinaryWriter;return proto.botStatusRequest.serializeBinaryToWriter(this,t),t.getResultBuffer()},proto.botStatusRequest.serializeBinaryToWriter=function(t,e){var o=void 0;o=t.getBotId(),o.length>0&&e.writeString(1,o),o=t.getX(),0!==o&&e.writeFloat(2,o),o=t.getY(),0!==o&&e.writeFloat(3,o),o=t.getEyeX(),0!==o&&e.writeFloat(4,o),o=t.getEyeY(),0!==o&&e.writeFloat(5,o),o=t.getMsg(),o.length>0&&e.writeString(6,o),o=t.getRealX(),0!==o&&e.writeFloat(7,o),o=t.getRealY(),0!==o&&e.writeFloat(8,o),o=t.getStatus(),0!==o&&e.writeEnum(9,o),o=t.getName(),o.length>0&&e.writeString(10,o),o=t.getGender(),0!==o&&e.writeEnum(11,o),o=t.getPosInfo(),null!=o&&e.writeMessage(12,o,proto.pInfo.serializeBinaryToWriter)},proto.botStatusRequest.status_type={WAITING:0,CONNECTING:1,CLOSE:2},proto.botStatusRequest.gender_type={MAN:0,WOMAN:1},proto.botStatusRequest.prototype.getBotId=function(){return r.Message.getFieldWithDefault(this,1,"")},proto.botStatusRequest.prototype.setBotId=function(t){return r.Message.setProto3StringField(this,1,t)},proto.botStatusRequest.prototype.getX=function(){return r.Message.getFloatingPointFieldWithDefault(this,2,0)},proto.botStatusRequest.prototype.setX=function(t){return r.Message.setProto3FloatField(this,2,t)},proto.botStatusRequest.prototype.getY=function(){return r.Message.getFloatingPointFieldWithDefault(this,3,0)},proto.botStatusRequest.prototype.setY=function(t){return r.Message.setProto3FloatField(this,3,t)},proto.botStatusRequest.prototype.getEyeX=function(){return r.Message.getFloatingPointFieldWithDefault(this,4,0)},proto.botStatusRequest.prototype.setEyeX=function(t){return r.Message.setProto3FloatField(this,4,t)},proto.botStatusRequest.prototype.getEyeY=function(){return r.Message.getFloatingPointFieldWithDefault(this,5,0)},proto.botStatusRequest.prototype.setEyeY=function(t){return r.Message.setProto3FloatField(this,5,t)},proto.botStatusRequest.prototype.getMsg=function(){return r.Message.getFieldWithDefault(this,6,"")},proto.botStatusRequest.prototype.setMsg=function(t){return r.Message.setProto3StringField(this,6,t)},proto.botStatusRequest.prototype.getRealX=function(){return r.Message.getFloatingPointFieldWithDefault(this,7,0)},proto.botStatusRequest.prototype.setRealX=function(t){return r.Message.setProto3FloatField(this,7,t)},proto.botStatusRequest.prototype.getRealY=function(){return r.Message.getFloatingPointFieldWithDefault(this,8,0)},proto.botStatusRequest.prototype.setRealY=function(t){return r.Message.setProto3FloatField(this,8,t)},proto.botStatusRequest.prototype.getStatus=function(){return r.Message.getFieldWithDefault(this,9,0)},proto.botStatusRequest.prototype.setStatus=function(t){return r.Message.setProto3EnumField(this,9,t)},proto.botStatusRequest.prototype.getName=function(){return r.Message.getFieldWithDefault(this,10,"")},proto.botStatusRequest.prototype.setName=function(t){return r.Message.setProto3StringField(this,10,t)},proto.botStatusRequest.prototype.getGender=function(){return r.Message.getFieldWithDefault(this,11,0)},proto.botStatusRequest.prototype.setGender=function(t){return r.Message.setProto3EnumField(this,11,t)},proto.botStatusRequest.prototype.getPosInfo=function(){return r.Message.getWrapperField(this,proto.pInfo,12)},proto.botStatusRequest.prototype.setPosInfo=function(t){return r.Message.setWrapperField(this,12,t)},proto.botStatusRequest.prototype.clearPosInfo=function(){return this.setPosInfo(void 0)},proto.botStatusRequest.prototype.hasPosInfo=function(){return null!=r.Message.getField(this,12)},proto.botStatusResponse.repeatedFields_=[1],r.Message.GENERATE_TO_OBJECT&&(proto.botStatusResponse.prototype.toObject=function(t){return proto.botStatusResponse.toObject(t,this)},proto.botStatusResponse.toObject=function(t,e){var o={botStatusList:r.Message.toObjectList(e.getBotStatusList(),proto.botStatusRequest.toObject,t)};return t&&(o.$jspbMessageInstance=e),o}),proto.botStatusResponse.deserializeBinary=function(t){var e=new r.BinaryReader(t),o=new proto.botStatusResponse;return proto.botStatusResponse.deserializeBinaryFromReader(o,e)},proto.botStatusResponse.deserializeBinaryFromReader=function(t,e){while(e.nextField()){if(e.isEndGroup())break;var o=e.getFieldNumber();switch(o){case 1:var r=new proto.botStatusRequest;e.readMessage(r,proto.botStatusRequest.deserializeBinaryFromReader),t.addBotStatus(r);break;default:e.skipField();break}}return t},proto.botStatusResponse.prototype.serializeBinary=function(){var t=new r.BinaryWriter;return proto.botStatusResponse.serializeBinaryToWriter(this,t),t.getResultBuffer()},proto.botStatusResponse.serializeBinaryToWriter=function(t,e){var o=void 0;o=t.getBotStatusList(),o.length>0&&e.writeRepeatedMessage(1,o,proto.botStatusRequest.serializeBinaryToWriter)},proto.botStatusResponse.prototype.getBotStatusList=function(){return r.Message.getRepeatedWrapperField(this,proto.botStatusRequest,1)},proto.botStatusResponse.prototype.setBotStatusList=function(t){return r.Message.setRepeatedWrapperField(this,1,t)},proto.botStatusResponse.prototype.addBotStatus=function(t,e){return r.Message.addToRepeatedWrapperField(this,1,t,proto.botStatusRequest,e)},proto.botStatusResponse.prototype.clearBotStatusList=function(){return this.setBotStatusList([])},n.object.extend(e,proto)}}); 2 | //# sourceMappingURL=app.26d1b8ef.js.map -------------------------------------------------------------------------------- /web_resource/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.19.2", 12 | "browserify": "^16.5.1", 13 | "chart.js": "^2.9.4", 14 | "core-js": "^3.6.4", 15 | "fpsmeter": "^0.3.1", 16 | "google-protobuf": "^3.11.4", 17 | "int32": "^2.5.5", 18 | "view-design": "^4.1.3", 19 | "vue": "^2.6.11", 20 | "vue-axios": "^2.1.5" 21 | }, 22 | "devDependencies": { 23 | "@vue/cli-plugin-babel": "^4.3.0", 24 | "@vue/cli-plugin-eslint": "^4.3.0", 25 | "@vue/cli-service": "^4.3.0", 26 | "babel-eslint": "^10.1.0", 27 | "eslint": "^6.7.2", 28 | "eslint-plugin-vue": "^6.2.2", 29 | "vue-template-compiler": "^2.6.11" 30 | }, 31 | "eslintConfig": { 32 | "root": true, 33 | "env": { 34 | "node": true 35 | }, 36 | "extends": [ 37 | "plugin:vue/essential" 38 | ], 39 | "parserOptions": { 40 | "parser": "babel-eslint" 41 | }, 42 | "rules": { 43 | "vue/no-parsing-error": [ 44 | 2, 45 | { 46 | "x-invalid-end-tag": false 47 | } 48 | ] 49 | } 50 | }, 51 | "browserslist": [ 52 | "> 1%", 53 | "last 2 versions", 54 | "not dead" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /web_resource/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/public/favicon.ico -------------------------------------------------------------------------------- /web_resource/public/image/human.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/public/image/human.png -------------------------------------------------------------------------------- /web_resource/public/image/m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/public/image/m.png -------------------------------------------------------------------------------- /web_resource/public/image/w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/public/image/w.png -------------------------------------------------------------------------------- /web_resource/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= htmlWebpackPlugin.options.title %> 8 | 9 | 10 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /web_resource/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 35 | -------------------------------------------------------------------------------- /web_resource/src/Space.js: -------------------------------------------------------------------------------- 1 | /** 2 | * author huajie.sun 3 | * from china 4 | * 2020年04月21日15:41:35 5 | * https://github.com/sunshinev/go-space-chat 6 | * license MIT 7 | * @type {null} 8 | */ 9 | import "google-protobuf" 10 | import "./proto/star_pb.js" 11 | import "fpsmeter" 12 | 13 | var ctx = null; 14 | var canvas = null; 15 | 16 | var points = []; 17 | 18 | // 每个空间产生的star数量 19 | var points_num = 200; 20 | 21 | var visual = { 22 | x: 0, 23 | y: 0, 24 | z: 10 25 | }; 26 | 27 | // 模拟物体真实尺寸 28 | var visual_star_size = 3; 29 | 30 | var keys = { 31 | up: 87, // w 32 | down: 83, // s 33 | left: 65, //a 34 | right: 68, //d 35 | talk: 32, //space 36 | send: 13 37 | }; 38 | 39 | // 矩形坐标 40 | var matrix_poi = { 41 | x: 0, 42 | y: 0 43 | }; 44 | 45 | // 三维粒子分布范围 46 | var points_scope = { 47 | x: { 48 | min: 0, 49 | max: 2000, 50 | }, 51 | y: { 52 | min: 0, 53 | max: 2000, 54 | }, 55 | z: { 56 | min: 0, 57 | max: 10, 58 | } 59 | }; 60 | 61 | // 移动速度 62 | var move_speed = 2; 63 | 64 | // 移动方向 65 | var move_direction = { 66 | up: false, 67 | down: false, 68 | left: false, 69 | right: false 70 | }; 71 | 72 | // star随机移动的方向和距离 73 | var random_points_update = [0.5, 0, 0]; 74 | var random_points_update_key = true; 75 | var random_points_update_status = 0; 76 | 77 | // 缩放值 78 | var zoom_scope_value = 0; 79 | 80 | // 当前画布序号 81 | var current_space = { 82 | x: 0, 83 | y: 0 84 | }; 85 | 86 | var mouse_poi = {x: 0, y: 0}; 87 | 88 | // 输入框dom 89 | var input = null; 90 | // 如果开启input,禁止移动,默认关闭 91 | var input_deny_move_key = false; 92 | // 输入的消息队列 93 | var input_messages_queue = []; 94 | var show_message_box = null; 95 | 96 | // websocket 97 | var ws = null; 98 | 99 | var bot_status = { 100 | x: 0, 101 | y: 0, 102 | e_x: 0, 103 | e_y: 0, 104 | r_x: 0, 105 | r_y: 0, 106 | bot_id: '', 107 | name: '', 108 | gender: 0 109 | }; 110 | 111 | // 状态记忆 112 | var bot_status_old = {}; 113 | 114 | var is_ws_open = false; 115 | 116 | var real_top_left_poi = { 117 | x: 0, 118 | y: 0 119 | }; 120 | 121 | // 维护客户列表 122 | var guest_bots = {}; 123 | var guest_show_message_box = {}; 124 | 125 | // 全局聊天窗口 126 | var globalChatWindow = document.createElement("div"); 127 | 128 | // fps 129 | FPSMeter.theme.dark.count.fontSize = '12px' 130 | var meter = new FPSMeter(); 131 | 132 | 133 | function initCtx() { 134 | canvas = document.getElementById("test"); 135 | canvas.width = window.innerWidth; 136 | canvas.height = window.innerHeight; 137 | 138 | ctx = canvas.getContext('2d'); 139 | 140 | ctx.shadowColor = "white"; 141 | ctx.shadowBlur = 10; 142 | 143 | matrix_poi = { 144 | x: canvas.width / 2, 145 | y: canvas.height / 2 146 | } 147 | 148 | // 初始化视点 149 | visual.x = canvas.width / 2; 150 | visual.y = canvas.height / 2; 151 | 152 | // 初始化粒子范围 153 | points_scope.x.max = canvas.width; 154 | points_scope.y.max = canvas.height; 155 | 156 | // 初始化粒子 157 | points = randomPoint(); 158 | 159 | bot_status.bot_id = Math.random().toString(36).substr(2); 160 | 161 | randomPointsUpdate() 162 | } 163 | 164 | function canvasHandle() { 165 | ctx.clearRect(0, 0, canvas.width, canvas.height); 166 | 167 | ctx.fillStyle = "rgb(20,7,34)"; 168 | ctx.fillRect(0, 0, canvas.width, canvas.height); 169 | 170 | immediateUpdate(); 171 | 172 | points = updatePoints(points, random_points_update); 173 | 174 | drawn(ctx, points); 175 | matrixMove(); 176 | 177 | // 维护状态 178 | bot_status.x = visual.x; 179 | bot_status.y = visual.y; 180 | 181 | // 尝试同步消息 182 | sendStatusByWs(); 183 | 184 | createGuestBot(); 185 | 186 | // fps 187 | meter.tick() 188 | 189 | window.requestAnimationFrame(canvasHandle); 190 | } 191 | 192 | // star 自然振动 193 | function randomPointsUpdate() { 194 | 195 | setInterval(function () { 196 | if (random_points_update_key) { 197 | random_points_update = [ 198 | Math.random() - 0.5, 199 | Math.random() - 0.5, 200 | 0 201 | ]; 202 | } 203 | }, 5000) 204 | 205 | } 206 | 207 | function immediateUpdate() { 208 | if (random_points_update_status) { 209 | random_points_update = [ 210 | Math.random() - 0.5, 211 | Math.random() - 0.5, 212 | 0 213 | ]; 214 | random_points_update_status = 0; 215 | } 216 | } 217 | 218 | function randomPoint() { 219 | 220 | // 计算每个space的宽高 221 | var width = points_scope.x.max - points_scope.x.min; 222 | var height = points_scope.y.max - points_scope.y.min; 223 | 224 | var p = []; 225 | for (var i = 0; i < points_num; i++) { 226 | var x = randomValue(current_space.x + width, current_space.x); 227 | var y = randomValue(current_space.y + height, current_space.y); 228 | var z = randomValue(points_scope.z.max, points_scope.z.min); 229 | // 根据z轴 色彩递减 230 | var c = computeColor(z); 231 | var s = computeSize(z); 232 | 233 | p.push({x: x, y: y, z: z, c: c, s: s}) 234 | } 235 | 236 | 237 | p.sort(function (a, b) { 238 | return a.z - b.z; 239 | }); 240 | 241 | return p; 242 | } 243 | 244 | 245 | function updatePoints(points, values = [0, 0, 0]) { 246 | 247 | 248 | for (var i in points) { 249 | var p = points[i]; 250 | p.x += values[0]; 251 | p.y += values[1]; 252 | p.z += values[2]; 253 | 254 | // 重要:实现无限star!这个地方要保证粒子的绘制范围x,y 在canvas之内 255 | if (p.x > canvas.width) { 256 | p.x -= canvas.width; 257 | } else if (p.x < 0) { 258 | p.x += canvas.width; 259 | } 260 | 261 | if (p.y > canvas.height) { 262 | p.y -= canvas.height; 263 | } else if (p.y < 0) { 264 | p.y += canvas.height; 265 | } 266 | 267 | points[i] = p; 268 | } 269 | 270 | return points; 271 | } 272 | 273 | function randomValue(max, min = 0) { 274 | return Math.floor(Math.random() * (max - min)) + min 275 | } 276 | 277 | // 颜色越来越深 278 | function computeColor(z) { 279 | var v = Math.floor((z * (255 - 100)) / (points_scope.z.max - points_scope.z.min)) + 100 280 | return "rgb(" + v + "," + v + "," + v + ")"; 281 | } 282 | 283 | // 尺寸越来越小 284 | function computeSize(z) { 285 | // 设定视点处,半径为10 286 | return z * visual_star_size / visual.z; 287 | } 288 | 289 | // 绘制star 290 | function drawn(ctx, points = []) { 291 | for (var i in points) { 292 | var p = pointConvert(points[i].x, points[i].y, points[i].z, points[i].c, points[i].s); 293 | // 这里要做一些舍弃动作,视野之外的粒子,不予绘制 294 | if (p.x < canvas.width || p.y < canvas.height) { 295 | drawnPoint(ctx, p.x, p.y, p.c, p.s) 296 | } 297 | } 298 | } 299 | 300 | // 核心转换算法 301 | function pointConvert(x, y, z, c, s) { 302 | var p = { 303 | x: (x - visual.x) * visual.z / (visual.z - z) + visual.x, 304 | y: (y - visual.y) * visual.z / (visual.z - z) + visual.y, 305 | c: c, 306 | s: s, 307 | } 308 | 309 | return p; 310 | } 311 | 312 | function drawnPoint(ctx, x, y, c, s) { 313 | ctx.beginPath(); 314 | ctx.arc(x, y, s, 0, 2 * Math.PI) 315 | ctx.fillStyle = c; 316 | ctx.fill() 317 | } 318 | 319 | // 绘制矩形 320 | function drawMatrix(x = canvas.width / 2, y = canvas.height / 2) { 321 | // 矩形 322 | // ctx.fillStyle = "rgb(200,0,0)" 323 | // ctx.fillRect(x, y, 50, 30); 324 | 325 | // 眼睛 326 | ctx.beginPath(); 327 | x = x + 5; 328 | y = y + 5; 329 | ctx.arc(x, y, 8, 0, 2 * Math.PI); 330 | if (bot_status.gender === proto.botStatusRequest.gender_type.WOMAN) { 331 | ctx.fillStyle = "rgb(255,20,147)"; 332 | } else { 333 | ctx.fillStyle = "rgb(0,191,255)"; 334 | } 335 | 336 | ctx.fill(); 337 | 338 | // 瞳孔 339 | ctx.beginPath() 340 | var pupil = moveEye(x, y, 8); 341 | 342 | // 维护botstatus eye 343 | bot_status.e_x = pupil[0]; 344 | bot_status.e_y = pupil[1]; 345 | 346 | ctx.arc(pupil[0], pupil[1], 4, 0, 2 * Math.PI); 347 | ctx.fillStyle = "rgb(255,255,255)"; 348 | ctx.fill() 349 | 350 | ctx.font = "14px Arial"; 351 | if (bot_status.gender === proto.botStatusRequest.gender_type.WOMAN) { 352 | ctx.fillStyle = "rgb(255,20,147)"; 353 | } else { 354 | ctx.fillStyle = "rgb(0,191,255)"; 355 | } 356 | ctx.fillText(bot_status.name, x - 8, y + 20) 357 | 358 | } 359 | 360 | 361 | // 绘制矩形 362 | function drawMatrixGuest(x = canvas.width / 2, y = canvas.height / 2, e_x, e_y, name, gender, city) { 363 | // 矩形 364 | // ctx.fillStyle = "rgb(200,0,0)" 365 | // ctx.fillRect(x, y, 50, 30); 366 | 367 | // 眼睛 368 | ctx.beginPath(); 369 | x = x + 5; 370 | y = y + 5; 371 | ctx.arc(x, y, 8, 0, 2 * Math.PI); 372 | 373 | if (gender === proto.botStatusRequest.gender_type.WOMAN) { 374 | ctx.fillStyle = "rgb(255,20,147)"; 375 | } else { 376 | ctx.fillStyle = "rgb(0,191,255)"; 377 | } 378 | 379 | ctx.fill(); 380 | 381 | // 瞳孔 382 | ctx.beginPath() 383 | 384 | ctx.arc(e_x, e_y, 4, 0, 2 * Math.PI); 385 | ctx.fillStyle = "rgb(255,255,255)"; 386 | ctx.fill() 387 | 388 | ctx.font = "14px Arial"; 389 | if (gender === proto.botStatusRequest.gender_type.WOMAN) { 390 | ctx.fillStyle = "rgb(255,20,147)"; 391 | } else { 392 | ctx.fillStyle = "rgb(0,191,255)"; 393 | } 394 | ctx.fillText(name, x - 8, y + 20) 395 | ctx.fillText(city, x - 8, y + 40) 396 | 397 | } 398 | 399 | // x,y 的坐标是眼眶的坐标,理论上来讲,这个地方应该用角度来计算位置 400 | function moveEye(x, y, r) { 401 | 402 | var r_pupil = 5; 403 | 404 | var e_x = (x - mouse_poi.x) * (r - r_pupil) / Math.sqrt(Math.pow(x - mouse_poi.x, 2) + Math.pow(y - mouse_poi.y, 2)); 405 | var e_y = (y - mouse_poi.y) * (r - r_pupil) / Math.sqrt(Math.pow(x - mouse_poi.x, 2) + Math.pow(y - mouse_poi.y, 2)); 406 | 407 | 408 | if (move_direction.up) { 409 | e_x = 0; 410 | e_y = r - r_pupil; 411 | } else if (move_direction.down) { 412 | e_x = 0; 413 | e_y = -(r - r_pupil); 414 | } 415 | 416 | if (move_direction.left) { 417 | e_x = r - r_pupil; 418 | e_y = 0; 419 | } else if (move_direction.right) { 420 | e_x = -(r - r_pupil); 421 | e_y = 0; 422 | } 423 | 424 | var dis = Math.sqrt(Math.pow(r - r_pupil, 2) / 2) 425 | if (move_direction.up && move_direction.left) { 426 | e_x = dis; 427 | e_y = dis; 428 | } else if (move_direction.up && move_direction.right) { 429 | e_x = -dis; 430 | e_y = dis; 431 | } else if (move_direction.down && move_direction.left) { 432 | e_x = dis; 433 | e_y = -dis; 434 | } else if (move_direction.down && move_direction.right) { 435 | e_x = -dis; 436 | e_y = -dis; 437 | } 438 | 439 | return [ 440 | x - e_x, 441 | y - e_y 442 | ]; 443 | } 444 | 445 | // 事件 446 | function bindEvent() { 447 | window.addEventListener('keydown', (evt) => { 448 | // 如果input框弹出,那么禁止其他键盘事件 449 | if (input_deny_move_key && evt.keyCode != keys.send) { 450 | return false; 451 | } 452 | 453 | // 缩放 454 | switch (evt.keyCode) { 455 | case keys.up: 456 | move_direction.up = true; 457 | move_direction.down = false; 458 | break; 459 | case keys.down: 460 | move_direction.up = false; 461 | move_direction.down = true; 462 | break; 463 | case keys.right: 464 | move_direction.right = true; 465 | move_direction.left = false; 466 | break; 467 | case keys.left: 468 | move_direction.left = true; 469 | move_direction.right = false; 470 | break; 471 | case keys.talk: 472 | createInput(); 473 | break; 474 | case keys.send: 475 | sendMessage(); 476 | break; 477 | } 478 | }) 479 | 480 | window.addEventListener('keyup', (evt) => { 481 | 482 | switch (evt.keyCode) { 483 | case keys.up: 484 | move_direction.up = false; 485 | break; 486 | case keys.down: 487 | move_direction.down = false; 488 | break; 489 | case keys.right: 490 | move_direction.right = false; 491 | break; 492 | case keys.left: 493 | move_direction.left = false; 494 | break; 495 | // talk pass 496 | } 497 | 498 | if (evt.keyCode == keys.up || evt.keyCode == keys.down || evt.keyCode == keys.left || evt.keyCode == keys.right) { 499 | random_points_update_key = true; 500 | random_points_update_status = 1; 501 | } 502 | }) 503 | 504 | // 获取当前鼠标位置 505 | window.addEventListener("mousemove", (evt) => { 506 | mouse_poi = { 507 | x: evt.x, y: evt.y 508 | } 509 | }); 510 | 511 | document.body.addEventListener('touchmove', (e) => { 512 | e.preventDefault(); 513 | }); 514 | } 515 | 516 | 517 | function createInput() { 518 | 519 | if (input != null) { 520 | return false; 521 | } 522 | 523 | input_deny_move_key = true; 524 | 525 | input = document.createElement("input"); 526 | input.setAttribute("style", "position:fixed;" + 527 | "left:" + (visual.x) + "px;" + 528 | "top:" + (visual.y + 30) + "px;" + 529 | "background-color:rgba(200,200,200,0.2);" + 530 | "border:1px solid rgba(200,200,200,0.2);" + 531 | "border-radius:10px;" + 532 | "padding:5px;" + 533 | "outline:none;" + 534 | "width:150px;" + 535 | "color:white;" + 536 | "font-size:12px" 537 | ); 538 | 539 | input.setAttribute('maxlength', 50); 540 | 541 | document.body.appendChild(input) 542 | input.addEventListener('focus', () => { 543 | }); 544 | 545 | input.addEventListener('blur', () => { 546 | document.body.removeChild(input); 547 | input = null; 548 | input_deny_move_key = false; 549 | }) 550 | 551 | input.focus() 552 | } 553 | 554 | // todo 555 | function sendMessage() { 556 | if (!input || !input.value) { 557 | return false; 558 | } 559 | 560 | var value = input.value; 561 | input.blur(); 562 | 563 | // 创建div 564 | // if (show_message_box == null) { 565 | // show_message_box = document.createElement("div"); 566 | // show_message_box.setAttribute("style", "position:fixed;" + 567 | // "left:" + (visual.x) + "px;" + 568 | // "bottom:" + (canvas.height - visual.y + 20) + "px;" + 569 | // "color:white;" + 570 | // "font-size:12px" 571 | // ); 572 | // document.body.appendChild(show_message_box) 573 | // } 574 | // 575 | // createMessageBubble(value) 576 | 577 | // 发送文字消息 578 | sendStatusByWs(value); 579 | } 580 | 581 | 582 | // 创建气泡 583 | function createMessageBubble(value) { 584 | var bubble = document.createElement('p') 585 | bubble.innerHTML = "" + value + ""; 586 | show_message_box.appendChild(bubble) 587 | 588 | setTimeout(() => { 589 | show_message_box.removeChild(bubble) 590 | }, 1000 * 8); 591 | } 592 | 593 | function createGuestBot() { 594 | for (var i in guest_bots) { 595 | if (i === bot_status.bot_id) { 596 | if (show_message_box == null) { 597 | show_message_box = document.createElement("div"); 598 | show_message_box.setAttribute("style", "position:fixed;" + 599 | "left:" + (visual.x) + "px;" + 600 | "bottom:" + (canvas.height - visual.y + 20) + "px;" + 601 | "color:white;" + 602 | "font-size:12px" 603 | ); 604 | document.body.appendChild(show_message_box) 605 | } 606 | if (guest_bots[i].msg) { 607 | createMessageBubble(guest_bots[i].msg) 608 | guest_bots[i].msg = ''; 609 | } 610 | }else if (i !== bot_status.bot_id && isShowGuest(guest_bots[i].r_x + guest_bots[i].x - real_top_left_poi.x, guest_bots[i].r_y + guest_bots[i].y - real_top_left_poi.y)) { 611 | drawMatrixGuest(guest_bots[i].r_x + guest_bots[i].x - real_top_left_poi.x, guest_bots[i].r_y + guest_bots[i].y - real_top_left_poi.y, 612 | guest_bots[i].r_x + guest_bots[i].e_x - real_top_left_poi.x, guest_bots[i].r_y + guest_bots[i].e_y - real_top_left_poi.y, 613 | guest_bots[i].name, guest_bots[i].gender,guest_bots[i].pos_info.getCity()) 614 | // console.info(guest_bots[i].show_message_box) 615 | showGuestMessage(i) 616 | // console.info(guest_bots[i].show_message_box) 617 | if (guest_bots[i].msg) { 618 | createMessageBubbleGuest(i, guest_bots[i].msg) 619 | guest_bots[i].msg = ''; 620 | } 621 | moveBubbleGuest(i) 622 | } 623 | } 624 | } 625 | 626 | /** 627 | * 如果guest不在视野范围之内,那么不进行绘制,节省绘制资源 628 | * @param guestX 629 | * @param guestY 630 | */ 631 | function isShowGuest(guestX, guestY) { 632 | if (guestX < 0) { 633 | return false; 634 | } 635 | 636 | if (guestY < 0) { 637 | return false; 638 | } 639 | 640 | if (guestX > canvas.width) { 641 | return false; 642 | } 643 | 644 | if (guestY > canvas.height) { 645 | return false; 646 | } 647 | 648 | return true; 649 | } 650 | 651 | 652 | function showGuestMessage(name) { 653 | // 创建div 654 | if (!guest_show_message_box[name]) { 655 | 656 | guest_show_message_box[name] = document.createElement("div"); 657 | 658 | guest_show_message_box[name].setAttribute("style", "position:fixed;" + 659 | "left:" + (guest_bots[name].x + guest_bots[name].r_x - real_top_left_poi.x) + "px;" + 660 | "bottom:" + (canvas.height - (guest_bots[name].y + guest_bots[name].r_y - real_top_left_poi.y) + 20) + "px;" + 661 | "color:white;" + 662 | "font-size:12px" 663 | ); 664 | document.body.appendChild(guest_show_message_box[name]) 665 | } 666 | } 667 | 668 | // 创建气泡 669 | function createMessageBubbleGuest(name, value) { 670 | let bot = guest_bots[name]; 671 | let show_message_box = guest_show_message_box[name]; 672 | 673 | var bubble = document.createElement('p') 674 | bubble.innerHTML = "" + value + ""; 675 | show_message_box.appendChild(bubble) 676 | 677 | setTimeout(() => { 678 | show_message_box.removeChild(bubble) 679 | }, 1000 * 15); 680 | } 681 | 682 | function moveBubbleGuest(name) { 683 | let bot = guest_bots[name]; 684 | let show_message_box = guest_show_message_box[name]; 685 | 686 | show_message_box.setAttribute("style", "position:fixed;" + 687 | "left:" + (bot.x + bot.r_x - real_top_left_poi.x) + "px;" + 688 | "bottom:" + (canvas.height - (bot.y + bot.r_y - real_top_left_poi.y) + 20) + "px;" + 689 | "color:white;" + 690 | "font-size:12px" 691 | ); 692 | } 693 | 694 | function moveBubble() { 695 | if (show_message_box != null) { 696 | show_message_box.setAttribute("style", "position:fixed;" + 697 | "left:" + (visual.x) + "px;" + 698 | "bottom:" + (canvas.height - visual.y + 20) + "px;" + 699 | "color:white;" + 700 | "font-size:12px" 701 | ); 702 | } 703 | } 704 | 705 | // 核心移动 706 | function matrixMove() { 707 | 708 | var poi_y = matrix_poi.y; 709 | var poi_x = matrix_poi.x; 710 | var x_speed = 0; 711 | var y_speed = 0; 712 | 713 | if (move_direction.up) { 714 | poi_y = matrix_poi.y - move_speed; 715 | y_speed = -move_speed; 716 | } else if (move_direction.down) { 717 | poi_y = matrix_poi.y + move_speed; 718 | y_speed = move_speed; 719 | } 720 | 721 | if (move_direction.left) { 722 | poi_x = matrix_poi.x - move_speed; 723 | x_speed = -move_speed; 724 | } else if (move_direction.right) { 725 | poi_x = matrix_poi.x + move_speed; 726 | x_speed = move_speed; 727 | } 728 | 729 | if (x_speed || y_speed) { 730 | zoom('far'); 731 | // 设定martix的移动边界,为半径 732 | var moveRaidus = 100; 733 | 734 | // 判断如果移动距离超过了canvas的中心moveRadius,那么停止移动,下一步进行star移动 735 | // 1. 移动star 736 | if (computeDistance(poi_x, poi_y, canvas.width / 2, canvas.height / 2) >= moveRaidus) { 737 | // 关闭随机移动 738 | random_points_update_key = false; 739 | random_points_update_status = 0; 740 | random_points_update = [ 741 | -x_speed, 742 | -y_speed, 743 | 0 744 | ]; 745 | 746 | real_top_left_poi.x += x_speed; 747 | real_top_left_poi.y += y_speed; 748 | 749 | bot_status.r_x = real_top_left_poi.x; 750 | bot_status.r_y = real_top_left_poi.y; 751 | 752 | } else { 753 | // 2. 移动矩形 754 | matrix_poi.y = poi_y; 755 | matrix_poi.x = poi_x; 756 | } 757 | } else { 758 | zoom('near'); 759 | } 760 | 761 | // 视点跟随 762 | visual.x = matrix_poi.x; 763 | visual.y = matrix_poi.y; 764 | 765 | // 气泡跟随 766 | moveBubble(); 767 | 768 | drawMatrix(matrix_poi.x, matrix_poi.y); 769 | } 770 | 771 | // 视角缩放 far near 772 | function zoom(direction = 'far') { 773 | // 步进灵敏度 774 | var acc = 0.1; 775 | // 平滑灵敏度 776 | var pacc = 5; 777 | // 变化范围 778 | var scope = 1; 779 | 780 | if (direction == 'far') { 781 | if (zoom_scope_value < Math.PI) { 782 | zoom_scope_value += acc; 783 | visual.z = visual.z + scope * Math.sin(zoom_scope_value) / pacc 784 | } 785 | } else if (direction == 'near') { 786 | if (zoom_scope_value > 0) { 787 | zoom_scope_value -= acc; 788 | visual.z = visual.z - scope * Math.sin(zoom_scope_value) / pacc 789 | } 790 | } 791 | } 792 | 793 | // 计算两点的距离 794 | function computeDistance(x, y, x1, y1) { 795 | return Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2)) 796 | } 797 | 798 | function createWebSocket() { 799 | ws = new WebSocket("ws://" + location.hostname + ":9000/ws") 800 | 801 | ws.binaryType = 'arraybuffer'; 802 | 803 | ws.onopen = function () { 804 | console.info("ws open") 805 | is_ws_open = true; 806 | }; 807 | 808 | ws.onmessage = function (evt) { 809 | var r = proto.botStatusResponse.deserializeBinary(evt.data) 810 | var bot_list = r.getBotStatusList(); 811 | 812 | for (var i in bot_list) { 813 | // 如果收到广播连接断开,那么删除元素 814 | if (bot_list[i].getStatus() === proto.botStatusRequest.status_type.CLOSE) { 815 | delete guest_bots[bot_list[i].getBotId()]; 816 | continue; 817 | } 818 | 819 | guest_bots[bot_list[i].getBotId()] = { 820 | x: bot_list[i].getX(), 821 | y: bot_list[i].getY(), 822 | e_x: bot_list[i].getEyeX(), 823 | e_y: bot_list[i].getEyeY(), 824 | r_x: bot_list[i].getRealX(), 825 | r_y: bot_list[i].getRealY(), 826 | msg: bot_list[i].getMsg(), 827 | name: bot_list[i].getName(), 828 | gender: bot_list[i].getGender(), 829 | pos_info : bot_list[i].getPosInfo(), 830 | }; 831 | 832 | addMessageToChatWindow(guest_bots[bot_list[i].getBotId()]) 833 | } 834 | }; 835 | 836 | ws.onclose = function () { 837 | console.info("ws close") 838 | } 839 | } 840 | 841 | // 状态同步的频率限制 842 | var rateKey = true 843 | 844 | function sendStatusByWs(msg = '') { 845 | 846 | if (!is_ws_open) { 847 | return false; 848 | } 849 | 850 | var is_open = false; 851 | 852 | if (!!bot_status_old) { 853 | for (var i in bot_status) { 854 | if (bot_status[i] !== bot_status_old[i]) { 855 | is_open = true; 856 | } 857 | } 858 | } else { 859 | is_open = true; 860 | } 861 | 862 | // 如果msg==“”那么同步动作的时候,限制50ms一次 863 | if (msg === "") { 864 | if (rateKey) { 865 | rateKey = false 866 | setTimeout(function(){rateKey = true},30) 867 | }else { 868 | return 869 | } 870 | } 871 | 872 | if (is_open || msg) { 873 | 874 | let chat = new proto.botStatusRequest(); 875 | 876 | chat.setBotId(bot_status.bot_id); 877 | // console.info(visual.x,visual.y,bot_status.e_x,bot_status.e_y) 878 | chat.setX(visual.x); 879 | chat.setY(visual.y); 880 | chat.setEyeX(bot_status.e_x); 881 | chat.setEyeY(bot_status.e_y); 882 | chat.setRealX(real_top_left_poi.x); 883 | chat.setRealY(real_top_left_poi.y); 884 | chat.setMsg(msg); 885 | chat.setName(bot_status.name); 886 | chat.setGender(bot_status.gender); 887 | 888 | ws.send(chat.serializeBinary()); 889 | 890 | Object.assign(bot_status_old, bot_status) 891 | 892 | // addMessageToChatWindow(bot_status.name,msg) 893 | } 894 | } 895 | 896 | 897 | function initLocalStorage() { 898 | var name = localStorage.getItem('star_name'); 899 | if (name !== null && name !== "") { 900 | bot_status.name = name; 901 | } else { 902 | bot_status.name = 'Guest' + Math.random().toString(36).substr(2) 903 | } 904 | 905 | var gender = localStorage.getItem('star_gender'); 906 | if (gender !== null) { 907 | bot_status.gender = parseInt(gender); 908 | } else { 909 | bot_status.gender = proto.botStatusRequest.gender_type.MAN 910 | } 911 | } 912 | 913 | function initTools() { 914 | var tool_box = document.createElement('div'); 915 | tool_box.setAttribute("style", "" + 916 | "position:fixed;" + 917 | "text-align:center;" + 918 | "left:5px;" + 919 | "top:50px;" + 920 | "width:30px;" + 921 | "height:200px;" + 922 | "background-color:rgba(0,0,0,0.5);" + 923 | "border:1px solid rgba(0,0,0,0.5);" + 924 | "border-radius:5px;"); 925 | document.body.appendChild(tool_box); 926 | 927 | let name = createBtn(tool_box, 'image/human.png', '点我修改昵称'); 928 | 929 | var input = null; 930 | 931 | name.addEventListener('click', (evt) => { 932 | 933 | if (input) { 934 | return false; 935 | } 936 | 937 | input = document.createElement('input'); 938 | 939 | input.setAttribute("style", "position:fixed;" + 940 | "left:50px;" + 941 | "top:50px;" + 942 | "background-color:white;" + 943 | "border:1px solid white;" + 944 | "border-radius:5px;" + 945 | "padding:5px;" + 946 | "outline:none;" + 947 | "width:150px;" + 948 | "font-size:12px" 949 | ); 950 | 951 | input.setAttribute('placeholder', '请输入昵称,长度10') 952 | input.setAttribute('maxlength', 10); 953 | 954 | document.body.appendChild(input) 955 | input.focus(); 956 | 957 | input.addEventListener('blur', () => { 958 | // 设置名称 959 | if (input.value !== "") { 960 | bot_status.name = input.value; 961 | localStorage.setItem('star_name', bot_status.name); 962 | } 963 | // 移除节点 964 | document.body.removeChild(input) 965 | input = null; 966 | }) 967 | }) 968 | 969 | let genderMan = createBtn(tool_box, 'image/m.png', '男生'); 970 | 971 | genderMan.addEventListener('click', (evt) => { 972 | bot_status.gender = proto.botStatusRequest.gender_type.MAN 973 | localStorage.setItem('star_gender', bot_status.gender); 974 | }); 975 | 976 | let genderWoman = createBtn(tool_box, 'image/w.png', '女生'); 977 | genderWoman.addEventListener('click', (evt) => { 978 | bot_status.gender = proto.botStatusRequest.gender_type.WOMAN 979 | localStorage.setItem('star_gender', bot_status.gender); 980 | }); 981 | 982 | 983 | } 984 | 985 | function createBtn(tool_box, src, title = '') { 986 | var button = document.createElement("img") 987 | button.setAttribute("style", "" + 988 | // "display:inline-block;" + 989 | "width:25px;" + 990 | "height:25px;" + 991 | // "background-color:rgba(200,200,0,0.5);" + 992 | "border:1px solid rgba(200,200,0,0.5);" + 993 | "color:white;" + 994 | "cursor:default;" + 995 | "border-radius:5px;"); 996 | 997 | button.setAttribute('src', src); 998 | button.setAttribute('title', title); 999 | 1000 | tool_box.appendChild(button); 1001 | 1002 | return button; 1003 | } 1004 | 1005 | function createGlobalChatWindow() { 1006 | globalChatWindow.setAttribute("style", "" + 1007 | "position:fixed;" + 1008 | "right:5px;" + 1009 | "bottom:200px;" + 1010 | "width:400px;" + 1011 | "height:70%;" + 1012 | "color:rgba(200,200,200,0.8);" + 1013 | "border:1px solid rgba(200,200,200,0.8);"+ 1014 | "cursor:default;" + 1015 | "overflow-y:auto;"+ 1016 | "border-radius:5px;"); 1017 | 1018 | document.body.appendChild(globalChatWindow) 1019 | } 1020 | 1021 | 1022 | function addSystemMessageToChatWindow(name,message) { 1023 | if (message.trim() === ""){ 1024 | return 1025 | } 1026 | var mDiv = document.createElement("div") 1027 | mDiv.setAttribute("style","" + 1028 | "margin:2px;" + 1029 | ""); 1030 | mDiv.innerHTML = ""+ 1031 | "
" + 1032 | ""+name+":" +message 1033 | "
" 1034 | ""; 1035 | 1036 | globalChatWindow.appendChild(mDiv) 1037 | globalChatWindow.scrollTop = globalChatWindow.scrollHeight 1038 | } 1039 | 1040 | function addMessageToChatWindow(bot) { 1041 | if (bot.msg.trim() === ""){ 1042 | return 1043 | } 1044 | var mDiv = document.createElement("div") 1045 | mDiv.setAttribute("style","" + 1046 | "margin:2px;" + 1047 | ""); 1048 | mDiv.innerHTML = ""+ 1049 | "
" + 1050 | "["+bot.pos_info.getCity()+bot.pos_info.getIsp()+"]" + 1051 | "@"+bot.name+":" +bot.msg 1052 | "
" 1053 | ""; 1054 | 1055 | globalChatWindow.appendChild(mDiv) 1056 | globalChatWindow.scrollTop = globalChatWindow.scrollHeight 1057 | } 1058 | 1059 | function welcome() { 1060 | var str = [ 1061 | "欢迎来到这个秘密的地方,茫茫人海,如果能在这里相遇,说明是一种缘分~", 1062 | "互动方式如下:", 1063 | "1. W A S D进行上下左右", 1064 | "2. 空格开启聊天框,回车发送消息", 1065 | "3. 左上角修改昵称、性别,点击空白修改成功", 1066 | "4. 新增用户上线频率全天分布图", 1067 | "git 地址:https://github.com/sunshinev/go-space-chat", 1068 | "前端 Vue+canvas+websocket+protobuf,后端 Golang+websocket+protobuf+goroutine", 1069 | ] 1070 | for (var id in str) { 1071 | addSystemMessageToChatWindow("管理员",str[id]) 1072 | } 1073 | } 1074 | 1075 | 1076 | export default function () { 1077 | createGlobalChatWindow(); 1078 | 1079 | welcome(); 1080 | 1081 | initCtx(); 1082 | bindEvent(); 1083 | initTools(); 1084 | initLocalStorage(); 1085 | createWebSocket(); 1086 | 1087 | 1088 | window.requestAnimationFrame(canvasHandle); 1089 | }; -------------------------------------------------------------------------------- /web_resource/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunshinev/go-space-chat/8e9e43a756b198ba1f7339d872d74a13f1735d9f/web_resource/src/assets/logo.png -------------------------------------------------------------------------------- /web_resource/src/components/Chart.vue: -------------------------------------------------------------------------------- 1 | 6 | 86 | -------------------------------------------------------------------------------- /web_resource/src/components/Donghua.vue: -------------------------------------------------------------------------------- 1 | 5 | 21 | -------------------------------------------------------------------------------- /web_resource/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | new Vue({ 5 | render: h => h(App), 6 | }).$mount('#app') 7 | -------------------------------------------------------------------------------- /web_resource/src/proto/star_pb.js: -------------------------------------------------------------------------------- 1 | // source: star.proto 2 | /** 3 | * @fileoverview 4 | * @enhanceable 5 | * @suppress {messageConventions} JS Compiler reports an error if a variable or 6 | * field starts with 'MSG_' and isn't a translatable message. 7 | * @public 8 | */ 9 | // GENERATED CODE -- DO NOT EDIT! 10 | 11 | var jspb = require('google-protobuf'); 12 | var goog = jspb; 13 | var global = Function('return this')(); 14 | 15 | goog.exportSymbol('proto.botStatusRequest', null, global); 16 | goog.exportSymbol('proto.botStatusRequest.gender_type', null, global); 17 | goog.exportSymbol('proto.botStatusRequest.status_type', null, global); 18 | goog.exportSymbol('proto.botStatusResponse', null, global); 19 | goog.exportSymbol('proto.pInfo', null, global); 20 | /** 21 | * Generated by JsPbCodeGenerator. 22 | * @param {Array=} opt_data Optional initial data array, typically from a 23 | * server response, or constructed directly in Javascript. The array is used 24 | * in place and becomes part of the constructed object. It is not cloned. 25 | * If no data is provided, the constructed object will be empty, but still 26 | * valid. 27 | * @extends {jspb.Message} 28 | * @constructor 29 | */ 30 | proto.pInfo = function(opt_data) { 31 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 32 | }; 33 | goog.inherits(proto.pInfo, jspb.Message); 34 | if (goog.DEBUG && !COMPILED) { 35 | /** 36 | * @public 37 | * @override 38 | */ 39 | proto.pInfo.displayName = 'proto.pInfo'; 40 | } 41 | /** 42 | * Generated by JsPbCodeGenerator. 43 | * @param {Array=} opt_data Optional initial data array, typically from a 44 | * server response, or constructed directly in Javascript. The array is used 45 | * in place and becomes part of the constructed object. It is not cloned. 46 | * If no data is provided, the constructed object will be empty, but still 47 | * valid. 48 | * @extends {jspb.Message} 49 | * @constructor 50 | */ 51 | proto.botStatusRequest = function(opt_data) { 52 | jspb.Message.initialize(this, opt_data, 0, -1, null, null); 53 | }; 54 | goog.inherits(proto.botStatusRequest, jspb.Message); 55 | if (goog.DEBUG && !COMPILED) { 56 | /** 57 | * @public 58 | * @override 59 | */ 60 | proto.botStatusRequest.displayName = 'proto.botStatusRequest'; 61 | } 62 | /** 63 | * Generated by JsPbCodeGenerator. 64 | * @param {Array=} opt_data Optional initial data array, typically from a 65 | * server response, or constructed directly in Javascript. The array is used 66 | * in place and becomes part of the constructed object. It is not cloned. 67 | * If no data is provided, the constructed object will be empty, but still 68 | * valid. 69 | * @extends {jspb.Message} 70 | * @constructor 71 | */ 72 | proto.botStatusResponse = function(opt_data) { 73 | jspb.Message.initialize(this, opt_data, 0, -1, proto.botStatusResponse.repeatedFields_, null); 74 | }; 75 | goog.inherits(proto.botStatusResponse, jspb.Message); 76 | if (goog.DEBUG && !COMPILED) { 77 | /** 78 | * @public 79 | * @override 80 | */ 81 | proto.botStatusResponse.displayName = 'proto.botStatusResponse'; 82 | } 83 | 84 | 85 | 86 | if (jspb.Message.GENERATE_TO_OBJECT) { 87 | /** 88 | * Creates an object representation of this proto. 89 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 90 | * Optional fields that are not set will be set to undefined. 91 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 92 | * For the list of reserved names please see: 93 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 94 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 95 | * JSPB instance for transitional soy proto support: 96 | * http://goto/soy-param-migration 97 | * @return {!Object} 98 | */ 99 | proto.pInfo.prototype.toObject = function(opt_includeInstance) { 100 | return proto.pInfo.toObject(opt_includeInstance, this); 101 | }; 102 | 103 | 104 | /** 105 | * Static version of the {@see toObject} method. 106 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 107 | * the JSPB instance for transitional soy proto support: 108 | * http://goto/soy-param-migration 109 | * @param {!proto.pInfo} msg The msg instance to transform. 110 | * @return {!Object} 111 | * @suppress {unusedLocalVariables} f is only used for nested messages 112 | */ 113 | proto.pInfo.toObject = function(includeInstance, msg) { 114 | var f, obj = { 115 | cityId: jspb.Message.getFieldWithDefault(msg, 1, 0), 116 | country: jspb.Message.getFieldWithDefault(msg, 2, ""), 117 | region: jspb.Message.getFieldWithDefault(msg, 3, ""), 118 | province: jspb.Message.getFieldWithDefault(msg, 4, ""), 119 | city: jspb.Message.getFieldWithDefault(msg, 5, ""), 120 | isp: jspb.Message.getFieldWithDefault(msg, 6, "") 121 | }; 122 | 123 | if (includeInstance) { 124 | obj.$jspbMessageInstance = msg; 125 | } 126 | return obj; 127 | }; 128 | } 129 | 130 | 131 | /** 132 | * Deserializes binary data (in protobuf wire format). 133 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 134 | * @return {!proto.pInfo} 135 | */ 136 | proto.pInfo.deserializeBinary = function(bytes) { 137 | var reader = new jspb.BinaryReader(bytes); 138 | var msg = new proto.pInfo; 139 | return proto.pInfo.deserializeBinaryFromReader(msg, reader); 140 | }; 141 | 142 | 143 | /** 144 | * Deserializes binary data (in protobuf wire format) from the 145 | * given reader into the given message object. 146 | * @param {!proto.pInfo} msg The message object to deserialize into. 147 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 148 | * @return {!proto.pInfo} 149 | */ 150 | proto.pInfo.deserializeBinaryFromReader = function(msg, reader) { 151 | while (reader.nextField()) { 152 | if (reader.isEndGroup()) { 153 | break; 154 | } 155 | var field = reader.getFieldNumber(); 156 | switch (field) { 157 | case 1: 158 | var value = /** @type {number} */ (reader.readInt32()); 159 | msg.setCityId(value); 160 | break; 161 | case 2: 162 | var value = /** @type {string} */ (reader.readString()); 163 | msg.setCountry(value); 164 | break; 165 | case 3: 166 | var value = /** @type {string} */ (reader.readString()); 167 | msg.setRegion(value); 168 | break; 169 | case 4: 170 | var value = /** @type {string} */ (reader.readString()); 171 | msg.setProvince(value); 172 | break; 173 | case 5: 174 | var value = /** @type {string} */ (reader.readString()); 175 | msg.setCity(value); 176 | break; 177 | case 6: 178 | var value = /** @type {string} */ (reader.readString()); 179 | msg.setIsp(value); 180 | break; 181 | default: 182 | reader.skipField(); 183 | break; 184 | } 185 | } 186 | return msg; 187 | }; 188 | 189 | 190 | /** 191 | * Serializes the message to binary data (in protobuf wire format). 192 | * @return {!Uint8Array} 193 | */ 194 | proto.pInfo.prototype.serializeBinary = function() { 195 | var writer = new jspb.BinaryWriter(); 196 | proto.pInfo.serializeBinaryToWriter(this, writer); 197 | return writer.getResultBuffer(); 198 | }; 199 | 200 | 201 | /** 202 | * Serializes the given message to binary data (in protobuf wire 203 | * format), writing to the given BinaryWriter. 204 | * @param {!proto.pInfo} message 205 | * @param {!jspb.BinaryWriter} writer 206 | * @suppress {unusedLocalVariables} f is only used for nested messages 207 | */ 208 | proto.pInfo.serializeBinaryToWriter = function(message, writer) { 209 | var f = undefined; 210 | f = message.getCityId(); 211 | if (f !== 0) { 212 | writer.writeInt32( 213 | 1, 214 | f 215 | ); 216 | } 217 | f = message.getCountry(); 218 | if (f.length > 0) { 219 | writer.writeString( 220 | 2, 221 | f 222 | ); 223 | } 224 | f = message.getRegion(); 225 | if (f.length > 0) { 226 | writer.writeString( 227 | 3, 228 | f 229 | ); 230 | } 231 | f = message.getProvince(); 232 | if (f.length > 0) { 233 | writer.writeString( 234 | 4, 235 | f 236 | ); 237 | } 238 | f = message.getCity(); 239 | if (f.length > 0) { 240 | writer.writeString( 241 | 5, 242 | f 243 | ); 244 | } 245 | f = message.getIsp(); 246 | if (f.length > 0) { 247 | writer.writeString( 248 | 6, 249 | f 250 | ); 251 | } 252 | }; 253 | 254 | 255 | /** 256 | * optional int32 city_id = 1; 257 | * @return {number} 258 | */ 259 | proto.pInfo.prototype.getCityId = function() { 260 | return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0)); 261 | }; 262 | 263 | 264 | /** 265 | * @param {number} value 266 | * @return {!proto.pInfo} returns this 267 | */ 268 | proto.pInfo.prototype.setCityId = function(value) { 269 | return jspb.Message.setProto3IntField(this, 1, value); 270 | }; 271 | 272 | 273 | /** 274 | * optional string country = 2; 275 | * @return {string} 276 | */ 277 | proto.pInfo.prototype.getCountry = function() { 278 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 2, "")); 279 | }; 280 | 281 | 282 | /** 283 | * @param {string} value 284 | * @return {!proto.pInfo} returns this 285 | */ 286 | proto.pInfo.prototype.setCountry = function(value) { 287 | return jspb.Message.setProto3StringField(this, 2, value); 288 | }; 289 | 290 | 291 | /** 292 | * optional string region = 3; 293 | * @return {string} 294 | */ 295 | proto.pInfo.prototype.getRegion = function() { 296 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 3, "")); 297 | }; 298 | 299 | 300 | /** 301 | * @param {string} value 302 | * @return {!proto.pInfo} returns this 303 | */ 304 | proto.pInfo.prototype.setRegion = function(value) { 305 | return jspb.Message.setProto3StringField(this, 3, value); 306 | }; 307 | 308 | 309 | /** 310 | * optional string province = 4; 311 | * @return {string} 312 | */ 313 | proto.pInfo.prototype.getProvince = function() { 314 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 4, "")); 315 | }; 316 | 317 | 318 | /** 319 | * @param {string} value 320 | * @return {!proto.pInfo} returns this 321 | */ 322 | proto.pInfo.prototype.setProvince = function(value) { 323 | return jspb.Message.setProto3StringField(this, 4, value); 324 | }; 325 | 326 | 327 | /** 328 | * optional string city = 5; 329 | * @return {string} 330 | */ 331 | proto.pInfo.prototype.getCity = function() { 332 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 5, "")); 333 | }; 334 | 335 | 336 | /** 337 | * @param {string} value 338 | * @return {!proto.pInfo} returns this 339 | */ 340 | proto.pInfo.prototype.setCity = function(value) { 341 | return jspb.Message.setProto3StringField(this, 5, value); 342 | }; 343 | 344 | 345 | /** 346 | * optional string isp = 6; 347 | * @return {string} 348 | */ 349 | proto.pInfo.prototype.getIsp = function() { 350 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); 351 | }; 352 | 353 | 354 | /** 355 | * @param {string} value 356 | * @return {!proto.pInfo} returns this 357 | */ 358 | proto.pInfo.prototype.setIsp = function(value) { 359 | return jspb.Message.setProto3StringField(this, 6, value); 360 | }; 361 | 362 | 363 | 364 | 365 | 366 | if (jspb.Message.GENERATE_TO_OBJECT) { 367 | /** 368 | * Creates an object representation of this proto. 369 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 370 | * Optional fields that are not set will be set to undefined. 371 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 372 | * For the list of reserved names please see: 373 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 374 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 375 | * JSPB instance for transitional soy proto support: 376 | * http://goto/soy-param-migration 377 | * @return {!Object} 378 | */ 379 | proto.botStatusRequest.prototype.toObject = function(opt_includeInstance) { 380 | return proto.botStatusRequest.toObject(opt_includeInstance, this); 381 | }; 382 | 383 | 384 | /** 385 | * Static version of the {@see toObject} method. 386 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 387 | * the JSPB instance for transitional soy proto support: 388 | * http://goto/soy-param-migration 389 | * @param {!proto.botStatusRequest} msg The msg instance to transform. 390 | * @return {!Object} 391 | * @suppress {unusedLocalVariables} f is only used for nested messages 392 | */ 393 | proto.botStatusRequest.toObject = function(includeInstance, msg) { 394 | var f, obj = { 395 | botId: jspb.Message.getFieldWithDefault(msg, 1, ""), 396 | x: jspb.Message.getFloatingPointFieldWithDefault(msg, 2, 0.0), 397 | y: jspb.Message.getFloatingPointFieldWithDefault(msg, 3, 0.0), 398 | eyeX: jspb.Message.getFloatingPointFieldWithDefault(msg, 4, 0.0), 399 | eyeY: jspb.Message.getFloatingPointFieldWithDefault(msg, 5, 0.0), 400 | msg: jspb.Message.getFieldWithDefault(msg, 6, ""), 401 | realX: jspb.Message.getFloatingPointFieldWithDefault(msg, 7, 0.0), 402 | realY: jspb.Message.getFloatingPointFieldWithDefault(msg, 8, 0.0), 403 | status: jspb.Message.getFieldWithDefault(msg, 9, 0), 404 | name: jspb.Message.getFieldWithDefault(msg, 10, ""), 405 | gender: jspb.Message.getFieldWithDefault(msg, 11, 0), 406 | posInfo: (f = msg.getPosInfo()) && proto.pInfo.toObject(includeInstance, f) 407 | }; 408 | 409 | if (includeInstance) { 410 | obj.$jspbMessageInstance = msg; 411 | } 412 | return obj; 413 | }; 414 | } 415 | 416 | 417 | /** 418 | * Deserializes binary data (in protobuf wire format). 419 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 420 | * @return {!proto.botStatusRequest} 421 | */ 422 | proto.botStatusRequest.deserializeBinary = function(bytes) { 423 | var reader = new jspb.BinaryReader(bytes); 424 | var msg = new proto.botStatusRequest; 425 | return proto.botStatusRequest.deserializeBinaryFromReader(msg, reader); 426 | }; 427 | 428 | 429 | /** 430 | * Deserializes binary data (in protobuf wire format) from the 431 | * given reader into the given message object. 432 | * @param {!proto.botStatusRequest} msg The message object to deserialize into. 433 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 434 | * @return {!proto.botStatusRequest} 435 | */ 436 | proto.botStatusRequest.deserializeBinaryFromReader = function(msg, reader) { 437 | while (reader.nextField()) { 438 | if (reader.isEndGroup()) { 439 | break; 440 | } 441 | var field = reader.getFieldNumber(); 442 | switch (field) { 443 | case 1: 444 | var value = /** @type {string} */ (reader.readString()); 445 | msg.setBotId(value); 446 | break; 447 | case 2: 448 | var value = /** @type {number} */ (reader.readFloat()); 449 | msg.setX(value); 450 | break; 451 | case 3: 452 | var value = /** @type {number} */ (reader.readFloat()); 453 | msg.setY(value); 454 | break; 455 | case 4: 456 | var value = /** @type {number} */ (reader.readFloat()); 457 | msg.setEyeX(value); 458 | break; 459 | case 5: 460 | var value = /** @type {number} */ (reader.readFloat()); 461 | msg.setEyeY(value); 462 | break; 463 | case 6: 464 | var value = /** @type {string} */ (reader.readString()); 465 | msg.setMsg(value); 466 | break; 467 | case 7: 468 | var value = /** @type {number} */ (reader.readFloat()); 469 | msg.setRealX(value); 470 | break; 471 | case 8: 472 | var value = /** @type {number} */ (reader.readFloat()); 473 | msg.setRealY(value); 474 | break; 475 | case 9: 476 | var value = /** @type {!proto.botStatusRequest.status_type} */ (reader.readEnum()); 477 | msg.setStatus(value); 478 | break; 479 | case 10: 480 | var value = /** @type {string} */ (reader.readString()); 481 | msg.setName(value); 482 | break; 483 | case 11: 484 | var value = /** @type {!proto.botStatusRequest.gender_type} */ (reader.readEnum()); 485 | msg.setGender(value); 486 | break; 487 | case 12: 488 | var value = new proto.pInfo; 489 | reader.readMessage(value,proto.pInfo.deserializeBinaryFromReader); 490 | msg.setPosInfo(value); 491 | break; 492 | default: 493 | reader.skipField(); 494 | break; 495 | } 496 | } 497 | return msg; 498 | }; 499 | 500 | 501 | /** 502 | * Serializes the message to binary data (in protobuf wire format). 503 | * @return {!Uint8Array} 504 | */ 505 | proto.botStatusRequest.prototype.serializeBinary = function() { 506 | var writer = new jspb.BinaryWriter(); 507 | proto.botStatusRequest.serializeBinaryToWriter(this, writer); 508 | return writer.getResultBuffer(); 509 | }; 510 | 511 | 512 | /** 513 | * Serializes the given message to binary data (in protobuf wire 514 | * format), writing to the given BinaryWriter. 515 | * @param {!proto.botStatusRequest} message 516 | * @param {!jspb.BinaryWriter} writer 517 | * @suppress {unusedLocalVariables} f is only used for nested messages 518 | */ 519 | proto.botStatusRequest.serializeBinaryToWriter = function(message, writer) { 520 | var f = undefined; 521 | f = message.getBotId(); 522 | if (f.length > 0) { 523 | writer.writeString( 524 | 1, 525 | f 526 | ); 527 | } 528 | f = message.getX(); 529 | if (f !== 0.0) { 530 | writer.writeFloat( 531 | 2, 532 | f 533 | ); 534 | } 535 | f = message.getY(); 536 | if (f !== 0.0) { 537 | writer.writeFloat( 538 | 3, 539 | f 540 | ); 541 | } 542 | f = message.getEyeX(); 543 | if (f !== 0.0) { 544 | writer.writeFloat( 545 | 4, 546 | f 547 | ); 548 | } 549 | f = message.getEyeY(); 550 | if (f !== 0.0) { 551 | writer.writeFloat( 552 | 5, 553 | f 554 | ); 555 | } 556 | f = message.getMsg(); 557 | if (f.length > 0) { 558 | writer.writeString( 559 | 6, 560 | f 561 | ); 562 | } 563 | f = message.getRealX(); 564 | if (f !== 0.0) { 565 | writer.writeFloat( 566 | 7, 567 | f 568 | ); 569 | } 570 | f = message.getRealY(); 571 | if (f !== 0.0) { 572 | writer.writeFloat( 573 | 8, 574 | f 575 | ); 576 | } 577 | f = message.getStatus(); 578 | if (f !== 0.0) { 579 | writer.writeEnum( 580 | 9, 581 | f 582 | ); 583 | } 584 | f = message.getName(); 585 | if (f.length > 0) { 586 | writer.writeString( 587 | 10, 588 | f 589 | ); 590 | } 591 | f = message.getGender(); 592 | if (f !== 0.0) { 593 | writer.writeEnum( 594 | 11, 595 | f 596 | ); 597 | } 598 | f = message.getPosInfo(); 599 | if (f != null) { 600 | writer.writeMessage( 601 | 12, 602 | f, 603 | proto.pInfo.serializeBinaryToWriter 604 | ); 605 | } 606 | }; 607 | 608 | 609 | /** 610 | * @enum {number} 611 | */ 612 | proto.botStatusRequest.status_type = { 613 | WAITING: 0, 614 | CONNECTING: 1, 615 | CLOSE: 2 616 | }; 617 | 618 | /** 619 | * @enum {number} 620 | */ 621 | proto.botStatusRequest.gender_type = { 622 | MAN: 0, 623 | WOMAN: 1 624 | }; 625 | 626 | /** 627 | * optional string bot_id = 1; 628 | * @return {string} 629 | */ 630 | proto.botStatusRequest.prototype.getBotId = function() { 631 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, "")); 632 | }; 633 | 634 | 635 | /** 636 | * @param {string} value 637 | * @return {!proto.botStatusRequest} returns this 638 | */ 639 | proto.botStatusRequest.prototype.setBotId = function(value) { 640 | return jspb.Message.setProto3StringField(this, 1, value); 641 | }; 642 | 643 | 644 | /** 645 | * optional float x = 2; 646 | * @return {number} 647 | */ 648 | proto.botStatusRequest.prototype.getX = function() { 649 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 2, 0.0)); 650 | }; 651 | 652 | 653 | /** 654 | * @param {number} value 655 | * @return {!proto.botStatusRequest} returns this 656 | */ 657 | proto.botStatusRequest.prototype.setX = function(value) { 658 | return jspb.Message.setProto3FloatField(this, 2, value); 659 | }; 660 | 661 | 662 | /** 663 | * optional float y = 3; 664 | * @return {number} 665 | */ 666 | proto.botStatusRequest.prototype.getY = function() { 667 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 3, 0.0)); 668 | }; 669 | 670 | 671 | /** 672 | * @param {number} value 673 | * @return {!proto.botStatusRequest} returns this 674 | */ 675 | proto.botStatusRequest.prototype.setY = function(value) { 676 | return jspb.Message.setProto3FloatField(this, 3, value); 677 | }; 678 | 679 | 680 | /** 681 | * optional float eye_x = 4; 682 | * @return {number} 683 | */ 684 | proto.botStatusRequest.prototype.getEyeX = function() { 685 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 4, 0.0)); 686 | }; 687 | 688 | 689 | /** 690 | * @param {number} value 691 | * @return {!proto.botStatusRequest} returns this 692 | */ 693 | proto.botStatusRequest.prototype.setEyeX = function(value) { 694 | return jspb.Message.setProto3FloatField(this, 4, value); 695 | }; 696 | 697 | 698 | /** 699 | * optional float eye_y = 5; 700 | * @return {number} 701 | */ 702 | proto.botStatusRequest.prototype.getEyeY = function() { 703 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 5, 0.0)); 704 | }; 705 | 706 | 707 | /** 708 | * @param {number} value 709 | * @return {!proto.botStatusRequest} returns this 710 | */ 711 | proto.botStatusRequest.prototype.setEyeY = function(value) { 712 | return jspb.Message.setProto3FloatField(this, 5, value); 713 | }; 714 | 715 | 716 | /** 717 | * optional string msg = 6; 718 | * @return {string} 719 | */ 720 | proto.botStatusRequest.prototype.getMsg = function() { 721 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 6, "")); 722 | }; 723 | 724 | 725 | /** 726 | * @param {string} value 727 | * @return {!proto.botStatusRequest} returns this 728 | */ 729 | proto.botStatusRequest.prototype.setMsg = function(value) { 730 | return jspb.Message.setProto3StringField(this, 6, value); 731 | }; 732 | 733 | 734 | /** 735 | * optional float real_x = 7; 736 | * @return {number} 737 | */ 738 | proto.botStatusRequest.prototype.getRealX = function() { 739 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 7, 0.0)); 740 | }; 741 | 742 | 743 | /** 744 | * @param {number} value 745 | * @return {!proto.botStatusRequest} returns this 746 | */ 747 | proto.botStatusRequest.prototype.setRealX = function(value) { 748 | return jspb.Message.setProto3FloatField(this, 7, value); 749 | }; 750 | 751 | 752 | /** 753 | * optional float real_y = 8; 754 | * @return {number} 755 | */ 756 | proto.botStatusRequest.prototype.getRealY = function() { 757 | return /** @type {number} */ (jspb.Message.getFloatingPointFieldWithDefault(this, 8, 0.0)); 758 | }; 759 | 760 | 761 | /** 762 | * @param {number} value 763 | * @return {!proto.botStatusRequest} returns this 764 | */ 765 | proto.botStatusRequest.prototype.setRealY = function(value) { 766 | return jspb.Message.setProto3FloatField(this, 8, value); 767 | }; 768 | 769 | 770 | /** 771 | * optional status_type status = 9; 772 | * @return {!proto.botStatusRequest.status_type} 773 | */ 774 | proto.botStatusRequest.prototype.getStatus = function() { 775 | return /** @type {!proto.botStatusRequest.status_type} */ (jspb.Message.getFieldWithDefault(this, 9, 0)); 776 | }; 777 | 778 | 779 | /** 780 | * @param {!proto.botStatusRequest.status_type} value 781 | * @return {!proto.botStatusRequest} returns this 782 | */ 783 | proto.botStatusRequest.prototype.setStatus = function(value) { 784 | return jspb.Message.setProto3EnumField(this, 9, value); 785 | }; 786 | 787 | 788 | /** 789 | * optional string name = 10; 790 | * @return {string} 791 | */ 792 | proto.botStatusRequest.prototype.getName = function() { 793 | return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 10, "")); 794 | }; 795 | 796 | 797 | /** 798 | * @param {string} value 799 | * @return {!proto.botStatusRequest} returns this 800 | */ 801 | proto.botStatusRequest.prototype.setName = function(value) { 802 | return jspb.Message.setProto3StringField(this, 10, value); 803 | }; 804 | 805 | 806 | /** 807 | * optional gender_type gender = 11; 808 | * @return {!proto.botStatusRequest.gender_type} 809 | */ 810 | proto.botStatusRequest.prototype.getGender = function() { 811 | return /** @type {!proto.botStatusRequest.gender_type} */ (jspb.Message.getFieldWithDefault(this, 11, 0)); 812 | }; 813 | 814 | 815 | /** 816 | * @param {!proto.botStatusRequest.gender_type} value 817 | * @return {!proto.botStatusRequest} returns this 818 | */ 819 | proto.botStatusRequest.prototype.setGender = function(value) { 820 | return jspb.Message.setProto3EnumField(this, 11, value); 821 | }; 822 | 823 | 824 | /** 825 | * optional pInfo pos_info = 12; 826 | * @return {?proto.pInfo} 827 | */ 828 | proto.botStatusRequest.prototype.getPosInfo = function() { 829 | return /** @type{?proto.pInfo} */ ( 830 | jspb.Message.getWrapperField(this, proto.pInfo, 12)); 831 | }; 832 | 833 | 834 | /** 835 | * @param {?proto.pInfo|undefined} value 836 | * @return {!proto.botStatusRequest} returns this 837 | */ 838 | proto.botStatusRequest.prototype.setPosInfo = function(value) { 839 | return jspb.Message.setWrapperField(this, 12, value); 840 | }; 841 | 842 | 843 | /** 844 | * Clears the message field making it undefined. 845 | * @return {!proto.botStatusRequest} returns this 846 | */ 847 | proto.botStatusRequest.prototype.clearPosInfo = function() { 848 | return this.setPosInfo(undefined); 849 | }; 850 | 851 | 852 | /** 853 | * Returns whether this field is set. 854 | * @return {boolean} 855 | */ 856 | proto.botStatusRequest.prototype.hasPosInfo = function() { 857 | return jspb.Message.getField(this, 12) != null; 858 | }; 859 | 860 | 861 | 862 | /** 863 | * List of repeated fields within this message type. 864 | * @private {!Array} 865 | * @const 866 | */ 867 | proto.botStatusResponse.repeatedFields_ = [1]; 868 | 869 | 870 | 871 | if (jspb.Message.GENERATE_TO_OBJECT) { 872 | /** 873 | * Creates an object representation of this proto. 874 | * Field names that are reserved in JavaScript and will be renamed to pb_name. 875 | * Optional fields that are not set will be set to undefined. 876 | * To access a reserved field use, foo.pb_, eg, foo.pb_default. 877 | * For the list of reserved names please see: 878 | * net/proto2/compiler/js/internal/generator.cc#kKeyword. 879 | * @param {boolean=} opt_includeInstance Deprecated. whether to include the 880 | * JSPB instance for transitional soy proto support: 881 | * http://goto/soy-param-migration 882 | * @return {!Object} 883 | */ 884 | proto.botStatusResponse.prototype.toObject = function(opt_includeInstance) { 885 | return proto.botStatusResponse.toObject(opt_includeInstance, this); 886 | }; 887 | 888 | 889 | /** 890 | * Static version of the {@see toObject} method. 891 | * @param {boolean|undefined} includeInstance Deprecated. Whether to include 892 | * the JSPB instance for transitional soy proto support: 893 | * http://goto/soy-param-migration 894 | * @param {!proto.botStatusResponse} msg The msg instance to transform. 895 | * @return {!Object} 896 | * @suppress {unusedLocalVariables} f is only used for nested messages 897 | */ 898 | proto.botStatusResponse.toObject = function(includeInstance, msg) { 899 | var f, obj = { 900 | botStatusList: jspb.Message.toObjectList(msg.getBotStatusList(), 901 | proto.botStatusRequest.toObject, includeInstance) 902 | }; 903 | 904 | if (includeInstance) { 905 | obj.$jspbMessageInstance = msg; 906 | } 907 | return obj; 908 | }; 909 | } 910 | 911 | 912 | /** 913 | * Deserializes binary data (in protobuf wire format). 914 | * @param {jspb.ByteSource} bytes The bytes to deserialize. 915 | * @return {!proto.botStatusResponse} 916 | */ 917 | proto.botStatusResponse.deserializeBinary = function(bytes) { 918 | var reader = new jspb.BinaryReader(bytes); 919 | var msg = new proto.botStatusResponse; 920 | return proto.botStatusResponse.deserializeBinaryFromReader(msg, reader); 921 | }; 922 | 923 | 924 | /** 925 | * Deserializes binary data (in protobuf wire format) from the 926 | * given reader into the given message object. 927 | * @param {!proto.botStatusResponse} msg The message object to deserialize into. 928 | * @param {!jspb.BinaryReader} reader The BinaryReader to use. 929 | * @return {!proto.botStatusResponse} 930 | */ 931 | proto.botStatusResponse.deserializeBinaryFromReader = function(msg, reader) { 932 | while (reader.nextField()) { 933 | if (reader.isEndGroup()) { 934 | break; 935 | } 936 | var field = reader.getFieldNumber(); 937 | switch (field) { 938 | case 1: 939 | var value = new proto.botStatusRequest; 940 | reader.readMessage(value,proto.botStatusRequest.deserializeBinaryFromReader); 941 | msg.addBotStatus(value); 942 | break; 943 | default: 944 | reader.skipField(); 945 | break; 946 | } 947 | } 948 | return msg; 949 | }; 950 | 951 | 952 | /** 953 | * Serializes the message to binary data (in protobuf wire format). 954 | * @return {!Uint8Array} 955 | */ 956 | proto.botStatusResponse.prototype.serializeBinary = function() { 957 | var writer = new jspb.BinaryWriter(); 958 | proto.botStatusResponse.serializeBinaryToWriter(this, writer); 959 | return writer.getResultBuffer(); 960 | }; 961 | 962 | 963 | /** 964 | * Serializes the given message to binary data (in protobuf wire 965 | * format), writing to the given BinaryWriter. 966 | * @param {!proto.botStatusResponse} message 967 | * @param {!jspb.BinaryWriter} writer 968 | * @suppress {unusedLocalVariables} f is only used for nested messages 969 | */ 970 | proto.botStatusResponse.serializeBinaryToWriter = function(message, writer) { 971 | var f = undefined; 972 | f = message.getBotStatusList(); 973 | if (f.length > 0) { 974 | writer.writeRepeatedMessage( 975 | 1, 976 | f, 977 | proto.botStatusRequest.serializeBinaryToWriter 978 | ); 979 | } 980 | }; 981 | 982 | 983 | /** 984 | * repeated botStatusRequest bot_status = 1; 985 | * @return {!Array} 986 | */ 987 | proto.botStatusResponse.prototype.getBotStatusList = function() { 988 | return /** @type{!Array} */ ( 989 | jspb.Message.getRepeatedWrapperField(this, proto.botStatusRequest, 1)); 990 | }; 991 | 992 | 993 | /** 994 | * @param {!Array} value 995 | * @return {!proto.botStatusResponse} returns this 996 | */ 997 | proto.botStatusResponse.prototype.setBotStatusList = function(value) { 998 | return jspb.Message.setRepeatedWrapperField(this, 1, value); 999 | }; 1000 | 1001 | 1002 | /** 1003 | * @param {!proto.botStatusRequest=} opt_value 1004 | * @param {number=} opt_index 1005 | * @return {!proto.botStatusRequest} 1006 | */ 1007 | proto.botStatusResponse.prototype.addBotStatus = function(opt_value, opt_index) { 1008 | return jspb.Message.addToRepeatedWrapperField(this, 1, opt_value, proto.botStatusRequest, opt_index); 1009 | }; 1010 | 1011 | 1012 | /** 1013 | * Clears the list making it empty but non-null. 1014 | * @return {!proto.botStatusResponse} returns this 1015 | */ 1016 | proto.botStatusResponse.prototype.clearBotStatusList = function() { 1017 | return this.setBotStatusList([]); 1018 | }; 1019 | 1020 | 1021 | goog.object.extend(exports, proto); 1022 | -------------------------------------------------------------------------------- /web_resource/src/space/offscreen.js: -------------------------------------------------------------------------------- 1 | function offsetScreenBody() { 2 | 3 | } 4 | 5 | function offsetScreenEye() { 6 | 7 | } -------------------------------------------------------------------------------- /web_resource/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath:"/", 3 | filenameHashing:true, 4 | }; --------------------------------------------------------------------------------