├── .idea ├── .gitignore ├── YinwuChat.iml └── vcs.xml ├── LICENSE ├── README.md ├── pom.xml ├── src └── main │ ├── java │ └── org │ │ └── lintx │ │ └── plugins │ │ └── yinwuchat │ │ ├── Const.java │ │ ├── Util │ │ ├── Gson.java │ │ ├── ItemUtil.java │ │ ├── MessageUtil.java │ │ └── ReflectionUtil.java │ │ ├── bukkit │ │ ├── Config.java │ │ ├── Listeners.java │ │ ├── MessageManage.java │ │ ├── YinwuChat.java │ │ └── commands │ │ │ ├── PrivateMessage.java │ │ │ └── YinwuChat.java │ │ ├── bungee │ │ ├── BatManage.java │ │ ├── Commands.java │ │ ├── IgnoreCommand.java │ │ ├── Listeners.java │ │ ├── MessageManage.java │ │ ├── RedisUtil.java │ │ ├── ShieldedManage.java │ │ ├── YinwuChat.java │ │ ├── announcement │ │ │ ├── Config.java │ │ │ ├── Task.java │ │ │ └── TaskConfig.java │ │ ├── config │ │ │ ├── Config.java │ │ │ ├── CoolQConfig.java │ │ │ ├── FormatConfig.java │ │ │ ├── PlayerConfig.java │ │ │ ├── RedisConfig.java │ │ │ └── TipsConfig.java │ │ ├── httpserver │ │ │ ├── NettyChannelMessageHelper.java │ │ │ ├── NettyHttpRequestHandler.java │ │ │ ├── NettyHttpServer.java │ │ │ ├── NettyWebSocketFrameHandler.java │ │ │ ├── WsClientHelper.java │ │ │ └── WsClientUtil.java │ │ └── json │ │ │ ├── InputBase.java │ │ │ ├── InputCheckToken.java │ │ │ ├── InputCoolQ.java │ │ │ ├── InputMessage.java │ │ │ ├── OutputCoolQ.java │ │ │ ├── OutputPlayerList.java │ │ │ ├── OutputServerMessage.java │ │ │ ├── RedisMessage.java │ │ │ └── RedisMessageType.java │ │ ├── chat │ │ ├── handle │ │ │ ├── BungeeAtPlayerHandle.java │ │ │ ├── ChatHandle.java │ │ │ ├── CoolQCodeHandle.java │ │ │ ├── CoolQEscapeHandle.java │ │ │ ├── EmojiHandle.java │ │ │ ├── ExtraDataHandle.java │ │ │ ├── HandleComponentCallback.java │ │ │ ├── HandleTextCallback.java │ │ │ ├── ItemShowHandle.java │ │ │ ├── LinkHandle.java │ │ │ ├── StylePermissionHandle.java │ │ │ └── StyleSymbolHandle.java │ │ └── struct │ │ │ ├── BukkitChatPlayer.java │ │ │ ├── BungeeChatPlayer.java │ │ │ ├── Chat.java │ │ │ ├── ChatPlayer.java │ │ │ ├── ChatSource.java │ │ │ ├── ChatStruct.java │ │ │ └── ChatType.java │ │ └── json │ │ ├── HandleConfig.java │ │ ├── Message.java │ │ ├── MessageFormat.java │ │ ├── PrivateMessage.java │ │ └── PublicMessage.java │ └── resources │ ├── bungee.yml │ └── plugin.yml └── web-source ├── package.json ├── src ├── index.html ├── index.js └── index.scss └── webpack.config.js /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /workspace.xml -------------------------------------------------------------------------------- /.idea/YinwuChat.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 lintx 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YinwuChat 说明文档 2 | 3 | ### 关于YinwuChat 4 | YinwuChat同时是Bungeecord插件和Spigot插件,主要功能有。 5 | 1. 跨服聊天同步 6 | 2. 跨服私聊(`/msg <玩家名> 消息`) 7 | 3. 跨服@(聊天内容中输入想@的玩家的名字,或名字的前面一部分,不区分大小写) 8 | 4. 跨服物品展示(聊天内容中输入`[i]`即可将手中的物品发送到聊天栏,输入`[i:x]`可以展示背包中x对应的物品栏的物品,物品栏为0-8,然后从背包左上角从左至右从上至下为9-35,装备栏为36-39,副手为40,一条消息中可以展示多个物品) 9 | 5. WebSocket,开启WebSocket后配合YinwuChat-Web(Web客户端)可以实现web、游戏内聊天同步 10 | 6. 关键词屏蔽 11 | 7. 使用酷Q和酷Q HTTP API来实现Q群聊天同步 12 | 13 | 注:你需要在你的Bungee服务端和这个Bungee接入的所有的Spigot服务端都安装这个插件 14 | 15 | 16 | ### Q群聊天同步 17 | 1. YinwuChat插件配置 18 | 1. 需要开启openwsserver 19 | 2. 将coolQGroup设置为你想同步的Q群的号码 20 | 3. 将coolQAccessToken设置为一个足够复杂足够长的字符串(推荐32位左右的随机字符串) 21 | 2. 安装酷Q HTTP API插件 22 | 1. 去 https://github.com/richardchien/coolq-http-api/releases/latest 下载最新版本的coolq-http-api,coolq-http-api具体的安装说明可以到 https://cqhttp.cc/docs/ 或 http://richardchien.gitee.io/coolq-http-api/docs/ 查看 23 | 2. 将coolq-http-api放到酷Q目录下的app目录下 24 | 3. 打开酷Q的应用管理界面,点击重载应用按钮 25 | 4. 找到“[未启用]HTTP API”,点它,然后点右边的启用按钮 26 | 5. 有提示的全部点“是” 27 | 6. 到酷Q目录下的“data\app\io.github.richardchien.coolqhttpapi\config”目录,下,打开你登录的QQ号对应的json文件(比如你登录的QQ号是10000,那文件名就是10000.json) 28 | 7. 将use_http修改为false(如果你没有其他应用需要使用的话) 29 | 8. 将use_ws_reverse修改为true(必须!) 30 | 9. 将ws_reverse_url修改为插件的websocket监听地址加端口(比如你端口是9000,酷Q和mc服务器在一台机器上就填 ws://127.0.0.1:9000/ws) 31 | 10. post_message_format请务必保证是"string" 32 | 11. 将enable_heartbeat设置为true 33 | 12. 增加一行 "ws_reverse_use_universal_client": true, 或者如果你的json文件中有ws_reverse_use_universal_client的话将它改为true(必须!) 34 | 13. 将access_token修改为和YinwuChat配置中的coolQAccessToken一致的内容 35 | 14. 右键酷Q主界面,选择应用-HTTP API-重启应用 36 | 37 | ### 跨BungeeCord聊天 38 | > 支持公屏聊天、私聊、at等所有功能,但是私聊和at等指定玩家的功能,被拒绝或忽略等情况下,本地提示可能不正确 39 | 1. 将新版本BungeeCord端配置文件的redisConfig.openRedis修改为true 40 | 2. redisConfig.ip修改为redis服务器的ip 41 | 3. redisConfig.port修改为redis服务器的端口 42 | 4. redisConfig.password修改为redis服务器的密码 43 | 5. redisConfig.selfName修改为每个BungeeCord端都不一样的一个字符串(插件内部标记消息来源及消息目的用,每个BungeeCord必须不一样,无其他要求) 44 | 6. 重新加载插件后,在一个BungeeCord端接入的玩家发送的消息可以在其他BungeeCord端接入的玩家处看到 45 | 46 | 47 | ### 配置文件 48 | YinwuChat-Bungeecord的配置文件内容为: 49 | 50 | ```yaml 51 | #是否开启WebSocket 52 | openwsserver: false 53 | 54 | #WebSocket监听端口 55 | wsport: 8888 56 | 57 | #WebSocket发送消息时间间隔(毫秒) 58 | wsCooldown: 1000 59 | 60 | #安装了BungeeAdminTools插件时, 61 | #在Web端发送消息,使用哪个服务器作为禁言/ban的服务器 62 | webBATserver: lobby 63 | 64 | #@玩家时的冷却时间(秒) 65 | atcooldown: 10 66 | 67 | #@全体玩家的关键词 68 | #默认为all,可以使用@all来@所有人 69 | #如果你有一个服务器叫做lobby 70 | #那么可以使用@lobbyall来@lobby服务器的所有人 71 | #@lobbyall可以简写为@lall或@loball等(服务器名前面一部分) 72 | atAllKey: all 73 | 74 | #链接识别正则表达式,符合该正则的聊天内容会被替换,并且可以点击 75 | linkRegex: ((https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]) 76 | 77 | #聊天屏蔽模式,目前1为将聊天内容替换为shieldedReplace的内容,其他为直接拦截 78 | shieldedMode: 1 79 | 80 | #多少秒内总共发送屏蔽关键词`shieldedKickCount`次就会被踢出服务器(包括web端) 81 | shieldedKickTime: 60 82 | 83 | #`shieldedKickTime`秒内发送屏蔽关键词多少次会被踢出服务器 84 | shieldedKickCount: 3 85 | 86 | #配置文件的版本,请勿修改 87 | configVersion: 4 88 | 89 | #从web页面发送消息到游戏中时禁用的样式代码 90 | webDenyStyle: klmnor 91 | 92 | #聊天内容屏蔽关键词,list格式,你需要自己添加这个设置 93 | shieldeds: 94 | #每个关键词一行 95 | - keyword 96 | 97 | tipsConfig: 98 | shieldedKickTip: 你因为发送屏蔽词语,被踢出服务器 99 | 100 | #聊天内容中含有屏蔽关键词时,整个消息会被替换为这个 101 | shieldedReplace: 富强、民主、文明、和谐、自由、平等、公正、法治、爱国、敬业、诚信、友善 102 | atyouselfTip: '&c你不能@你自己' 103 | atyouTip: '&e{player}&b@了你' 104 | cooldownTip: '&c每次使用@功能之间需要等待10秒' 105 | ignoreTip: '&c对方忽略了你,并向你仍了一个烤土豆' 106 | banatTip: '&c对方不想被@,只想安安静静的做一个美男子' 107 | toPlayerNoOnlineTip: '&c对方不在线,无法发送私聊' 108 | msgyouselfTip: '&c你不能私聊你自己' 109 | youismuteTip: '&c你正在禁言中,不能说话' 110 | youisbanTip: '&c你被ban了,不能说话' 111 | 112 | #发送的聊天消息中含有屏蔽的关键词时会收到的提醒 113 | shieldedTip: '&c发送的信息中有被屏蔽的词语,无法发送,继续发送将被踢出服务器' 114 | 115 | #聊天内容中的链接将被替换为这个文本 116 | linkText: '&7[&f&l链接&r&7]&r' 117 | 118 | #各种消息的格式化设置 119 | formatConfig: 120 | #WebSocket发送过来的消息格式化内容, 121 | #由list构成,每段内容都分message、hover、click 3项设置 122 | format: 123 | #直接显示在聊天栏的文字, 124 | #{displayName}将被替换为玩家名 125 | #hover和click字段中的{displayName}也会替换 126 | - message: '&b[Web]' 127 | #鼠标移动到这段消息上时显示的悬浮内容 128 | hover: 点击打开YinwuChat网页 129 | #点击这段消息时的动作,自动识别是否链接,如果是链接则打开链接 130 | #否则如果是以!开头就执行命令,否则就将内容填充到聊天框 131 | #比如让看到消息的人点击就直接给发消息的人发送tpa请求, 132 | #就可以写成!/tpa {displayName}(不写斜杠会按发送消息处理) 133 | click: https://chat.yinwurealm.org 134 | - message: '&e{displayName}' 135 | hover: 点击私聊 136 | click: /msg {displayName} 137 | - message: ' &6>>> ' 138 | - message: '&r{message}' 139 | 140 | #QQ群群员发送的消息,游戏内展示的样式 141 | qqFormat: 142 | - message: '&b[QQ群]' 143 | hover: 点击加入QQ群xxxxx 144 | #这里可以替换为你QQ群的申请链接 145 | click: https://xxxxxx.xxxx.xxx 146 | - message: '&e{displayName}' 147 | - message: ' &6>>> ' 148 | - message: '&r{message}' 149 | 150 | #私聊时,自己收到的消息的格式 151 | toFormat: 152 | - message: '&7我 &6-> ' 153 | - message: '&e{displayName}' 154 | hover: 点击私聊 155 | click: /msg {displayName} 156 | - message: ' &6>>> ' 157 | - message: '&r{message}' 158 | 159 | #私聊时,对方收到的消息的格式 160 | fromFormat: 161 | - message: '&b[Web]' 162 | hover: 点击打开YinwuChat网页 163 | click: https://xxxxxx.xxxx.xxx 164 | - message: '&e{displayName}' 165 | hover: 点击私聊 166 | click: /msg {displayName} 167 | - message: ' &6-> &7我' 168 | - message: ' &6>>> ' 169 | - message: '&r{message}' 170 | 171 | #其他玩家私聊时,有权限的玩家看到的监听消息的样式 172 | monitorFormat: 173 | - message: '&7{formPlayer} &6-> ' 174 | - message: '&e{toPlayer}' 175 | - message: ' &6>>> ' 176 | - message: '&r{message}' 177 | coolQConfig: 178 | #qq群有新消息时是否发送到游戏中 179 | coolQQQToGame: true 180 | 181 | #qq群有新消息时,只有开头跟这里一样才发送到游戏中 182 | coolqToGameStart: '' 183 | 184 | #游戏中有新消息时是否发送到QQ群中 185 | coolQGameToQQ: true 186 | 187 | #游戏中有新消息时,只有开头跟这里一样才发送到QQ群中 188 | gameToCoolqStart: '' 189 | 190 | #转发QQ群消息到游戏时禁用的样式代码 191 | qqDenyStyle: 0-9a-fklmnor 192 | 193 | #监听的QQ群的群号,酷Q接收到消息时,如果是QQ群,且群号和这里一致,就会转发到游戏中 194 | coolQGroup: 0 195 | 196 | #和酷Q HTTP API插件通信时使用的accesstoken,为空时不验证,强烈建议设置为一个复杂的字符串 197 | coolQAccessToken: '' 198 | 199 | #QQ群中群员发送的@信息将被替换为这个文本 200 | #{qq}将被替换为被@的人的QQ号 201 | qqAtText: '&7[@{qq}]&r' 202 | 203 | #QQ群中群员发送的图片将被替换为这个文本 204 | qqImageText: '&7[图片]&r' 205 | 206 | #QQ群中群员发送的语音将被替换为这个文本 207 | qqRecordText: '&7[语音]&r' 208 | 209 | #利用redis做跨bc聊天同步的配置 210 | redisConfig: 211 | #是否开启redis聊天同步 212 | openRedis: false 213 | 214 | #redis服务器的ip地址或域名 215 | ip: '' 216 | 217 | #redis的端口 218 | port: 0 219 | 220 | #一般不要修改 221 | maxConnection: 8 222 | 223 | #redis的密码 224 | password: '' 225 | 226 | #服务器标识,每个bc端的YinwuChat插件的标识请设置为不一样 227 | selfName: bc1 228 | ``` 229 | `webBATserver`可以实现WebSocket端的禁言(当你的服务器安装了BungeeAdminTools时,玩家在WebSocket发送信息,会以这个项目的内容作为玩家所在服务器, 230 | 去BungeeAdminTools查询该玩家是否被禁言或被ban,当他被禁言或被ban时无法说话,由于BungeeAdminTools禁言、ban人只能选择Bungee的配置文件中实际存在的服务器, 231 | 所以这里需要填一个实际存在的服务器的名字,建议使用大厅服的名字) 232 | 233 | Bungee-Task配置文件(tasks.yml): 234 | ```yaml 235 | tasks: 236 | - enable: true #是否开启这个任务 237 | interval: 30 #任务间隔时间 238 | list: #格式和Bungee的配置文件中的消息格式一致 239 | - message: '&e[帮助]' 240 | hover: 服务器帮助文档 241 | click: '' 242 | - message: '&r 在聊天中输入' 243 | - message: '&b[i]' 244 | hover: 在聊天文本中包含这三个字符即可 245 | click: '' 246 | - message: '&r可以展示你手中的物品,输入' 247 | - message: '&b[i:x]' 248 | hover: |- 249 | &b:&r冒号不区分中英文 250 | &bx&r为背包格子编号 251 | 物品栏为0-8,然后从背包左上角 252 | 从左至右从上至下为9-35 253 | 装备栏为36-39,副手为40 254 | click: '' 255 | - message: '&r可以展示背包中x位置对应的物品,一条消息中可以展示多个物品' 256 | server: all #任务对应的服务器,不区分大小写,只有对应的服务器的玩家才会收到消息,为"all"时所有服务器都会广播,为"web"时只有web端才会收到通知 257 | ``` 258 | 259 | YinwuChat-Spigot的配置文件内容为: 260 | 261 | ```yaml 262 | format: #格式和Bungee的配置文件中的消息格式一致,但是这里的内容支持PlaceholderAPI变量 263 | - message: '&b[%player_server%]' 264 | hover: 所在服务器:%player_server% 265 | click: /server %player_server% 266 | - message: '&e{displayName}' 267 | hover: 点击私聊 268 | click: /msg {displayName} 269 | - message: ' &6>>> ' 270 | - message: '&r{message}' 271 | toFormat: 272 | - message: '&b[%player_server%]' 273 | hover: 所在服务器:%player_server% 274 | click: /server %player_server% 275 | - message: '&e{displayName}' 276 | hover: 点击私聊 277 | click: /msg {displayName} 278 | - message: ' &6-> &7我' 279 | - message: '&r{message}' 280 | fromFormat: 281 | - message: '&7我 &6-> ' 282 | - message: '&e{displayName}' 283 | hover: 点击私聊 284 | click: /msg {displayName} 285 | - message: '&r{message}' 286 | eventDelayTime: 50 #接收消息处理延时,单位为毫秒,用于处理部分需要使用聊天栏信息来交互的插件的运行(比如箱子商店等),延时时间就是等待其他插件处理的时间 287 | messageHandles: #自定义消息内容替换,比如下面默认的设置,发送消息时,消息中含有[p]的,[p]会被替换为位置 288 | - placeholder: \[p\] #消息中的哪些内容会被替换,写法是正则表达式,所以本来是替换[p]的,由于是正则表达式,两个方括号都需要加反斜杠转义 289 | format: #替换成的消息样式,格式和前面的format格式一致,支持papi变量 290 | - message: '&7[位置]' 291 | hover: |- 292 | 所在服务器:ServerName 293 | 所在世界:%player_world% 294 | 坐标:X:%player_x% Y:%player_y% Z:%player_z% 295 | click: '' 296 | configVersion: 1 #配置文件的版本,请勿修改 297 | ``` 298 | 299 | 300 | ### 接口 301 | 302 | 本插件所有信息均由WebSocket通信,格式均为JSON格式,具体数据如下: 303 | #### 发往本插件的数据: 304 | 1. 检查token 305 | ```json 306 | { 307 | "action": "check_token", 308 | "token": "待检查的token,token由服务器下发,初次连接时可以使用空字符串" 309 | } 310 | ``` 311 | 2. 发送消息 312 | ```json 313 | { 314 | "action": "send_message", 315 | "message": "需要发送的消息,注意,格式代码必须使用§" 316 | } 317 | ``` 318 | 319 | #### 发往Web客户端的数据: 320 | 1. 更新token(接收到客户端发送的check_token数据,然后检查token失败时下发,收到该数据应提醒玩家在游戏内输入/yinwuchat token title命令绑定token) 321 | ```json 322 | { 323 | "action": "update_token", 324 | "token": "一个随机的token" 325 | } 326 | ``` 327 | 2. token校验结果(检查token成功后返回,或玩家在游戏内绑定成功后,token对应的WebSocket在线时主动发送,只有接收到了这个数据,且数据中的status为true,且数据中的isbind为true时才可以向服务器发送send_message数据) 328 | ```json5 329 | { 330 | "action": "check_token", 331 | "status": true, //表示该token是否有效 332 | "message": "成功时为success,失败时为原因,并同时发送一个更新token数据", 333 | "isbind": false //表示该token是否被玩家绑定 334 | } 335 | ``` 336 | 3. 玩家在游戏内发送了消息 337 | ```json 338 | { 339 | "action": "send_message", 340 | "message": "消息内容" 341 | } 342 | ``` 343 | 4. 游戏玩家列表 344 | ```json 345 | { 346 | "action": "game_player_list", 347 | "player_list":[ 348 | { 349 | "player_name": "玩家游戏名", 350 | "server_name": "玩家所在服务器" 351 | } 352 | ] 353 | } 354 | ``` 355 | 5. WebClient玩家列表 356 | ```json 357 | { 358 | "action": "web_player_list", 359 | "player_list":[ 360 | "玩家名1", 361 | "玩家名2" 362 | ] 363 | } 364 | ``` 365 | 6. 服务器提示消息(一般为和服务器发送数据包后的错误反馈信息) 366 | ```json5 367 | { 368 | "action": "server_message", 369 | "message": "消息内容", 370 | "time": 1, //unix时间戳, 371 | "status": 1 //状态码,详情见下方表格(int) 372 | } 373 | ``` 374 | 375 | #### 服务器消息状态码 376 | 状态码|具体含义 377 | -:|- 378 | 0|一般成功或提示消息 379 | 1|一般错误消息 380 | 1001|获取历史聊天记录时,内容为空(不可继续获取历史消息) 381 | 382 | ### Bungeecord端命令 383 | 1. 控制台命令 384 | - `yinwuchat reload [config|ws]`:重新加载插件,或仅重新加载配置(在ws配置有变动时自动重启ws),或只重启ws 385 | 2. 游戏内命令 386 | - `/yinwuchat`:插件帮助(其他未识别的命令也都将显示帮助) 387 | - `/yinwuchat reload [config|ws]`:重新加载配置文件,执行这个命令需要你具有`yinwuchat.reload`权限 388 | - `/yinwuchat bind `:绑定token,`token`是插件下发给web客户端的,玩家从web客户端获取token后到游戏内使用命令将玩家和token进行绑定 389 | - `/yinwuchat list`:列出玩家已绑定的token 390 | - `/yinwuchat unbind `:解绑token,当你需要解绑某个token时使用(如在公共场合绑定了token,或者不想用这个token了等),`token`为使用`list`命令时查询到的`token`,可以只输入前面部分文字 391 | - `/msg <玩家名> <消息>`:向玩家发送私聊消息 392 | - `/yinwuchat vanish`:切换聊天系统隐身模式(无法被@,无法被私聊,web端无法看见在线,需要有`yinwuchat.vanish`权限) 393 | - `/yinwuchat ignore <玩家名>`:忽略/取消忽略玩家消息 394 | - `/yinwuchat noat`:禁止/允许自己被@(@全体除外) 395 | - `/yinwuchat muteat`:切换自己被@时有没有声音 396 | - `/yinwuchat monitor`:切换是否监听其他玩家的私聊消息 397 | 3. WebClient命令 398 | - `/msg <玩家名> <消息>`:向玩家发送私聊消息 399 | 400 | ### Bukkit端命令 401 | `yinwuchat-bukkit reload`: 重新加载配置,需要权限`yinwuchat.reload 402 | 403 | ### Bungee端权限 404 | - `yinwuchat.reload`玩家可以在游戏中使用`/yinwuchat reload`命令重新加载插件配置 405 | - `yinwuchat.cooldown.bypass`@人没有冷却时间 406 | - `yinwuchat.atall`允许@所有人 407 | - `yinwuchat.vanish`允许进入聊天隐身模式 408 | - `yinwuchat.badword`允许编辑聊天系统关键词列表 409 | - `yinwuchat.monitor`允许玩家使用`/yinwuchat monitor`命令,并允许玩家监听其他玩家的私聊消息 410 | * 权限需要在Bungeecord中设置,玩家可以在Bungeecord连接到的任何服务器使用这个命令 411 | 412 | ### Bukkit端权限 413 | `yinwuchat.reload`玩家可以在游戏中使用`/yinwuchat-bukkit reload`命令重新加载bukkit端yinwuchat的配置,默认权限:仅OP可以使用 414 | `yinwuchat.style.x`是否允许玩家使用对应的样式代码,`x`为具体样式代码,具体为`0-9`,`a-f`,`klmnor`共22个样式代码,默认设置时`0-9`,`a-f`,`r`为允许,其他为不允许 415 | 416 | ### @所有人 417 | @所有人可以@整个服务器所有人(不包括WebSocket),或者分服务器@该服务器所有人(不包括WebSocket) 418 | 具体使用方法为: 419 | 假如配置文件中的`atAllKey`是默认的`all`,那么聊天内容中含有`@all`时即可@整个服务器的人(all后面不能紧接着英文或数字,可以是中文、空格等) 420 | 假如你有一个服务器名字为`lobby`,那么聊天内容中含有`@lall`或`@lobbyall`时,即可@lobby服务器的所有人(即服务器名只需要输入前面部分即可,该服务器名为BungeeCord配置文件中的名字) 421 | 422 | ### 错误信息 423 | 有些时候,玩家执行命令的时候可能会碰到一些错误(主要为数据库错误),具体含义为: 424 | 425 | 错误代码|具体含义 426 | -:|- 427 | 001|根据UUID查找用户失败,且新增失败 428 | 429 | ### 其他信息 430 | 本插件由国内正版Minecraft服务器[YinwuRealm](https://www.yinwurealm.org/)玩家[LinTx](https://mine.ly/LinTx.1)为服务器开发 -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.lintx.plugins.yinwuchat 8 | YinwuChat 9 | 2.12 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 3.1 17 | 18 | 1.8 19 | 1.8 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-shade-plugin 25 | 3.1.0 26 | 27 | 28 | package 29 | 30 | shade 31 | 32 | 33 | 34 | 35 | org.bstats 36 | 37 | org.lintx.plugins.yinwuchat.maven.bstats 38 | 39 | 40 | org.lintx.plugins.modules 41 | org.lintx.plugins.yinwuchat.maven.modules 42 | 43 | 44 | 45 | redis.clients.jedis 46 | org.lintx.plugins.yinwuchat.maven.jedis 47 | 48 | 49 | redis.clients.util 50 | org.lintx.plugins.yinwuchat.maven.jedisutil 51 | 52 | 53 | org.apache.commons.pool 54 | org.lintx.plugins.yinwuchat.maven.commonspool 55 | 56 | 57 | true 58 | 59 | 60 | org.bstats 61 | org.lintx.plugins.modules 62 | redis.clients:jedis 63 | org.apache.commons:commons-pool2 64 | 65 | 66 | 67 | 68 | org.apache.commons:commons-pool2 69 | 70 | org/apache/commons/pool2/** 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | true 82 | src/main/resources 83 | 84 | 85 | 86 | 87 | 88 | 89 | ali-repo 90 | https://maven.aliyun.com/repository/central 91 | 92 | 93 | bungeecord-repo 94 | https://hub.spigotmc.org/nexus/content/groups/public/ 95 | 96 | 97 | placeholderapi 98 | https://repo.extendedclip.com/content/repositories/placeholderapi/ 99 | 100 | 101 | CodeMC 102 | https://repo.codemc.io/repository/maven-public/ 103 | 104 | 105 | 106 | 107 | org.projectlombok 108 | lombok 109 | 1.18.22 110 | provided 111 | 112 | 113 | org.lintx.plugins.modules 114 | Configure 115 | 1.2.5 116 | 117 | 118 | net.md-5 119 | bungeecord-api 120 | 1.16-R0.2-SNAPSHOT 121 | 122 | 123 | net.md-5 124 | bungeecord-api 125 | 1.16-R0.2-SNAPSHOT 126 | sources 127 | 128 | 129 | org.spigotmc 130 | spigot 131 | 1.13.2 132 | system 133 | ${project.basedir}/lib/spigot-1.13.2.jar 134 | 135 | 136 | org.spigotmc1 137 | spigot1 138 | 1.13.2 139 | system 140 | ${project.basedir}/lib/CMI8.3.9.0.jar 141 | 142 | 143 | me.clip 144 | placeholderapi 145 | 2.9.2 146 | provided 147 | 148 | 149 | fr.Alphart 150 | bungeeadmintools 151 | 1.13.2 152 | system 153 | ${project.basedir}/lib/BungeeAdminTools-1.5.4-premium.jar 154 | 155 | 156 | org.bstats 157 | bstats-bungeecord 158 | 1.5 159 | 160 | 161 | 162 | 163 | redis.clients 164 | jedis 165 | 2.9.0 166 | 167 | 168 | org.apache.commons 169 | commons-pool2 170 | 2.5.0 171 | 172 | 173 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/Const.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat; 2 | 3 | public class Const { 4 | public final static String ITEM_PLACEHOLDER = "\\[i([::]?)(\\d+)?\\]"; 5 | public final static String PLUGIN_CHANNEL = "BungeeCord"; 6 | public final static String PLUGIN_SUB_CHANNEL_AT = "org.lintx.plugins.yinwuchat:at"; 7 | public final static String PLUGIN_SUB_CHANNEL_PLAYER_LIST = "org.lintx.plugins.yinwuchat:player_list"; 8 | public final static String PLUGIN_SUB_CHANNEL_MESSAGE = "org.lintx.plugins.yinwuchat:chat"; 9 | public final static String PLUGIN_SUB_CHANNEL_PUBLIC_MESSAGE = "org.lintx.plugins.yinwuchat:chat"; 10 | public final static String PLUGIN_SUB_CHANNEL_PRIVATE_MESSAGE = "org.lintx.plugins.yinwuchat:msg"; 11 | 12 | public final static String PERMISSION_RELOAD = "yinwuchat.reload"; 13 | public final static String PERMISSION_BAD_WORD = "yinwuchat.badword"; 14 | public final static String PERMISSION_AT_ALL = "yinwuchat.atall"; 15 | public final static String PERMISSION_VANISH = "yinwuchat.vanish"; 16 | public final static String PERMISSION_COOL_DOWN_BYPASS = "yinwuchat.cooldown.bypass"; 17 | public final static String PERMISSION_MONITOR_PRIVATE_MESSAGE = "yinwuchat.monitor"; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/Util/Gson.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.Util; 2 | 3 | public class Gson { 4 | private static com.google.gson.Gson gson = new com.google.gson.Gson(); 5 | public static com.google.gson.Gson gson(){ 6 | return gson; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/Util/ItemUtil.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.Util; 2 | 3 | import net.md_5.bungee.api.chat.BaseComponent; 4 | import net.md_5.bungee.api.chat.HoverEvent; 5 | import net.md_5.bungee.api.chat.TextComponent; 6 | import net.md_5.bungee.api.chat.TranslatableComponent; 7 | import net.md_5.bungee.chat.ComponentSerializer; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.Material; 10 | import org.bukkit.block.ShulkerBox; 11 | import org.bukkit.inventory.ItemStack; 12 | import org.bukkit.inventory.meta.BlockStateMeta; 13 | import org.bukkit.inventory.meta.BookMeta; 14 | import org.bukkit.inventory.meta.ItemMeta; 15 | 16 | import java.lang.reflect.Method; 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.Collections; 20 | import java.util.List; 21 | import java.util.logging.Level; 22 | 23 | public class ItemUtil { 24 | private static String convertItemToJson(ItemStack itemStack){ 25 | // ItemStack methods to get a net.minecraft.server.ItemStack object for serialization 26 | Class> craftItemStackClazz = ReflectionUtil.getOBCClass("inventory.CraftItemStack"); 27 | Method asNMSCopyMethod = ReflectionUtil.getMethod(craftItemStackClazz, "asNMSCopy", ItemStack.class); 28 | 29 | // NMS Method to serialize a net.minecraft.server.ItemStack to a valid Json string 30 | Class> nmsItemStackClazz = ReflectionUtil.getNMSClass("ItemStack"); 31 | Class> nbtTagCompoundClazz = ReflectionUtil.getNMSClass("NBTTagCompound"); 32 | Method saveNmsItemStackMethod = ReflectionUtil.getMethod(nmsItemStackClazz, "save", nbtTagCompoundClazz); 33 | 34 | Object nmsNbtTagCompoundObj; // This will just be an empty NBTTagCompound instance to invoke the saveNms method 35 | Object nmsItemStackObj; // This is the net.minecraft.server.ItemStack object received from the asNMSCopy method 36 | Object itemAsJsonObject; // This is the net.minecraft.server.ItemStack after being put through saveNmsItem method 37 | String result; 38 | 39 | try { 40 | nmsNbtTagCompoundObj = nbtTagCompoundClazz.newInstance(); 41 | nmsItemStackObj = asNMSCopyMethod.invoke(null, itemStack); 42 | itemAsJsonObject = saveNmsItemStackMethod.invoke(nmsItemStackObj, nmsNbtTagCompoundObj); 43 | result = itemAsJsonObject.toString(); 44 | } catch (Throwable t) { 45 | Bukkit.getLogger().log(Level.SEVERE, "failed to serialize itemstack to nms item", t); 46 | return null; 47 | } 48 | 49 | // Return a string representation of the serialized object 50 | return result; 51 | } 52 | 53 | private static String getItemKey(ItemStack itemStack){ 54 | // ItemStack methods to get a net.minecraft.server.ItemStack object for serialization 55 | Class> craftItemStackClazz = ReflectionUtil.getOBCClass("inventory.CraftItemStack"); 56 | Method asNMSCopyMethod = ReflectionUtil.getMethod(craftItemStackClazz, "asNMSCopy", ItemStack.class); 57 | 58 | // NMS Method to serialize a net.minecraft.server.ItemStack to a valid Json string 59 | Class> nmsItemStackClazz = ReflectionUtil.getNMSClass("ItemStack"); 60 | 61 | Method getItemMethod = ReflectionUtil.getMethod(nmsItemStackClazz,"getItem"); 62 | Class> nmsItemClazz = ReflectionUtil.getNMSClass("Item"); 63 | Method getNameMethod = ReflectionUtil.getMethod(nmsItemClazz,"getName"); 64 | 65 | Object nmsItemStackObj; // This is the net.minecraft.server.ItemStack object received from the asNMSCopy method 66 | Object nmsItemObj; 67 | Object nmsItemName; 68 | String result; 69 | 70 | try { 71 | nmsItemStackObj = asNMSCopyMethod.invoke(null, itemStack); 72 | 73 | nmsItemObj = getItemMethod.invoke(nmsItemStackObj); 74 | nmsItemName = getNameMethod.invoke(nmsItemObj); 75 | result = nmsItemName.toString(); 76 | } catch (Throwable t) { 77 | Bukkit.getLogger().log(Level.SEVERE, "failed to serialize itemstack to nms item", t); 78 | return null; 79 | } 80 | 81 | return result; 82 | } 83 | 84 | //color:ItemStack.u().e.name(); 85 | private static BaseComponent getItemComponent(ItemStack itemStack){ 86 | 87 | TextComponent component = new TextComponent(); 88 | if (itemStack.hasItemMeta()){ 89 | ItemMeta itemMeta = itemStack.getItemMeta(); 90 | if (itemMeta.hasDisplayName()){ 91 | TextComponent textComponent = new TextComponent(itemMeta.getDisplayName()); 92 | component.addExtra(textComponent); 93 | return component; 94 | } 95 | } 96 | String nmsName = getItemKey(itemStack); 97 | if (nmsName!=null && !nmsName.equals("")){ 98 | TranslatableComponent itemComponent = new TranslatableComponent(nmsName); 99 | component.addExtra(itemComponent); 100 | return component; 101 | } 102 | TextComponent textComponent = new TextComponent(itemStack.getType().name()); 103 | component.addExtra(textComponent); 104 | return component; 105 | } 106 | 107 | public static BaseComponent componentWithPlayer(ItemStack itemStack){ 108 | if (itemStack==null || itemStack.getType().equals(Material.AIR)){ 109 | return null; 110 | } 111 | ItemStack item = itemStack.clone(); 112 | try { 113 | if (item.getType().equals(Material.WRITABLE_BOOK) || item.getType().equals(Material.WRITTEN_BOOK)){ 114 | BookMeta bm = (BookMeta)item.getItemMeta(); 115 | bm.setPages(Collections.emptyList()); 116 | item.setItemMeta(bm); 117 | } 118 | } 119 | catch (Exception | Error ignored){ 120 | 121 | } 122 | try { 123 | List shulker_boxes = new ArrayList<>(Arrays.asList(Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX, 124 | Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX, 125 | Material.LIGHT_BLUE_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX, 126 | Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX, 127 | Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX)); 128 | if (shulker_boxes.contains(item.getType())){ 129 | if (item.hasItemMeta()){ 130 | BlockStateMeta bsm = (BlockStateMeta)item.getItemMeta(); 131 | if (bsm.hasBlockState()){ 132 | ShulkerBox sb = (ShulkerBox)bsm.getBlockState(); 133 | for (ItemStack i:sb.getInventory()){ 134 | if (i==null){ 135 | continue; 136 | } 137 | if (i.getType().equals(Material.AIR)){ 138 | continue; 139 | } 140 | if (!i.hasItemMeta()){ 141 | continue; 142 | } 143 | ItemMeta im = Bukkit.getItemFactory().getItemMeta(i.getType()); 144 | ItemMeta original = i.getItemMeta(); 145 | if (original.hasDisplayName()){ 146 | im.setDisplayName(original.getDisplayName()); 147 | } 148 | i.setItemMeta(im); 149 | } 150 | bsm.setBlockState(sb); 151 | } 152 | item.setItemMeta(bsm); 153 | } 154 | } 155 | } 156 | catch (Exception | Error ignored){ 157 | 158 | } 159 | 160 | TextComponent component = new TextComponent(""); 161 | 162 | component.addExtra("§r§7[§r"); 163 | try { 164 | component.addExtra(getItemComponent(item)); 165 | } 166 | catch (Exception | Error e){ 167 | component.addExtra(item.getType().name()); 168 | } 169 | if (item.getAmount()>1){ 170 | component.addExtra(" x" + item.getAmount()); 171 | } 172 | component.addExtra("§r§7]§r"); 173 | 174 | 175 | String itemJson = convertItemToJson(itemStack); 176 | BaseComponent[] hoverEventComponents = new BaseComponent[]{ 177 | new TextComponent(itemJson) 178 | }; 179 | 180 | HoverEvent event = new HoverEvent(HoverEvent.Action.SHOW_ITEM,hoverEventComponents); 181 | component.setHoverEvent(event); 182 | 183 | return component; 184 | } 185 | 186 | public static String itemJsonWithPlayer(ItemStack itemStack){ 187 | BaseComponent component = componentWithPlayer(itemStack); 188 | if (component==null) return null; 189 | return ComponentSerializer.toString(component); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/Util/MessageUtil.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.Util; 2 | 3 | import net.md_5.bungee.api.ChatColor; 4 | import net.md_5.bungee.api.chat.TextComponent; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | public class MessageUtil { 13 | 14 | public static String filter(String message,String denyStyle){ 15 | message = message.replaceAll("&[" + denyStyle + "]",""); 16 | message = message.replaceAll("§[" + denyStyle + "]",""); 17 | return message; 18 | } 19 | 20 | public static String filterRGB(String message){ 21 | message = message.replaceAll("([0-9a-fA-F]{6}|[0-9a-fA-F]{3})",""); 22 | message = message.replaceAll("§#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})",""); 23 | return message; 24 | 25 | } 26 | 27 | public static String replace(String str){ 28 | return str.replaceAll("&([0-9a-fA-Fk-oK-OrRxX])","§$1"); 29 | } 30 | 31 | public static TextComponent newTextComponent(String str){ 32 | List strings = new ArrayList<>(); 33 | String regexp = "([0-9a-fA-F]{6}|[0-9a-fA-F]{3})"; 34 | Pattern pattern = Pattern.compile(regexp); 35 | Matcher matcher = pattern.matcher(str); 36 | while (matcher.find()){ 37 | String[] splits = str.split(regexp,2); 38 | if (splits.length!=2) break; 39 | 40 | strings.add(splits[0]); 41 | //这里处理匹配到的颜色代码 42 | try { 43 | String c = matcher.group(1); 44 | if (null==c){ 45 | strings.add(matcher.group(0)); 46 | }else { 47 | StringBuilder buffer = new StringBuilder(); 48 | buffer.append("#"); 49 | int l = c.length(); 50 | for (int i=0;i iterator = strings.iterator(); 72 | while (iterator.hasNext()){ 73 | String s = iterator.next(); 74 | TextComponent c = new TextComponent(); 75 | if (s.length()==7 && s.startsWith("#")){ 76 | c.setColor(ChatColor.of(s)); 77 | while (iterator.hasNext()){ 78 | String s1 = iterator.next(); 79 | if (s1.length()!=7 || !s1.startsWith("#")){ 80 | c.setText(s1); 81 | break; 82 | } 83 | } 84 | }else { 85 | c.setText(s); 86 | } 87 | component.addExtra(c); 88 | } 89 | return component; 90 | } 91 | 92 | public static String removeEmoji(String str){ 93 | return str.replaceAll("[^\\p{L}\\p{N}\\p{P}\\p{Z}]",""); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/Util/ReflectionUtil.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.Util; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.entity.Player; 5 | 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Method; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class ReflectionUtil { 13 | /* 14 | * The server version string to location NMS & OBC classes 15 | */ 16 | private static String versionString; 17 | 18 | /* 19 | * Cache of NMS classes that we've searched for 20 | */ 21 | private static Map> loadedNMSClasses = new HashMap>(); 22 | 23 | /* 24 | * Cache of OBS classes that we've searched for 25 | */ 26 | private static Map> loadedOBCClasses = new HashMap>(); 27 | 28 | /* 29 | * Cache of methods that we've found in particular classes 30 | */ 31 | private static Map, Map> loadedMethods = new HashMap, Map>(); 32 | 33 | /* 34 | * Cache of fields that we've found in particular classes 35 | */ 36 | private static Map, Map> loadedFields = new HashMap, Map>(); 37 | 38 | /** 39 | * Gets the version string for NMS & OBC class paths 40 | * 41 | * @return The version string of OBC and NMS packages 42 | */ 43 | public static String getVersion() { 44 | if (versionString == null) { 45 | String name = Bukkit.getServer().getClass().getPackage().getName(); 46 | versionString = name.substring(name.lastIndexOf('.') + 1) + "."; 47 | } 48 | 49 | return versionString; 50 | } 51 | 52 | /** 53 | * Get an NMS Class 54 | * 55 | * @param nmsClassName The name of the class 56 | * @return The class 57 | */ 58 | public static Class> getNMSClass(String nmsClassName) { 59 | if (loadedNMSClasses.containsKey(nmsClassName)) { 60 | return loadedNMSClasses.get(nmsClassName); 61 | } 62 | 63 | String clazzName = "net.minecraft.server." + getVersion() + nmsClassName; 64 | Class> clazz; 65 | 66 | try { 67 | clazz = Class.forName(clazzName); 68 | } catch (Throwable t) { 69 | t.printStackTrace(); 70 | return loadedNMSClasses.put(nmsClassName, null); 71 | } 72 | 73 | loadedNMSClasses.put(nmsClassName, clazz); 74 | return clazz; 75 | } 76 | 77 | /** 78 | * Get a class from the org.bukkit.craftbukkit package 79 | * 80 | * @param obcClassName the path to the class 81 | * @return the found class at the specified path 82 | */ 83 | public synchronized static Class> getOBCClass(String obcClassName) { 84 | if (loadedOBCClasses.containsKey(obcClassName)) { 85 | return loadedOBCClasses.get(obcClassName); 86 | } 87 | 88 | String clazzName = "org.bukkit.craftbukkit." + getVersion() + obcClassName; 89 | Class> clazz; 90 | 91 | try { 92 | clazz = Class.forName(clazzName); 93 | } catch (Throwable t) { 94 | t.printStackTrace(); 95 | loadedOBCClasses.put(obcClassName, null); 96 | return null; 97 | } 98 | 99 | loadedOBCClasses.put(obcClassName, clazz); 100 | return clazz; 101 | } 102 | 103 | /** 104 | * Get a Bukkit {@link Player} players NMS playerConnection object 105 | * 106 | * @param player The player 107 | * @return The players connection 108 | */ 109 | public static Object getConnection(Player player) { 110 | Method getHandleMethod = getMethod(player.getClass(), "getHandle"); 111 | 112 | if (getHandleMethod != null) { 113 | try { 114 | Object nmsPlayer = getHandleMethod.invoke(player); 115 | Field playerConField = getField(nmsPlayer.getClass(), "playerConnection"); 116 | return playerConField.get(nmsPlayer); 117 | } catch (Exception e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | return null; 123 | } 124 | 125 | /** 126 | * Get a classes constructor 127 | * 128 | * @param clazz The constructor class 129 | * @param params The parameters in the constructor 130 | * @return The constructor object 131 | */ 132 | public static Constructor> getConstructor(Class> clazz, Class>... params) { 133 | try { 134 | return clazz.getConstructor(params); 135 | } catch (NoSuchMethodException e) { 136 | return null; 137 | } 138 | } 139 | 140 | /** 141 | * Get a method from a class that has the specific paramaters 142 | * 143 | * @param clazz The class we are searching 144 | * @param methodName The name of the method 145 | * @param params Any parameters that the method has 146 | * @return The method with appropriate paramaters 147 | */ 148 | public static Method getMethod(Class> clazz, String methodName, Class>... params) { 149 | if (!loadedMethods.containsKey(clazz)) { 150 | loadedMethods.put(clazz, new HashMap()); 151 | } 152 | 153 | Map methods = loadedMethods.get(clazz); 154 | 155 | if (methods.containsKey(methodName)) { 156 | return methods.get(methodName); 157 | } 158 | 159 | try { 160 | Method method = clazz.getMethod(methodName, params); 161 | methods.put(methodName, method); 162 | loadedMethods.put(clazz, methods); 163 | return method; 164 | } catch (Exception e) { 165 | e.printStackTrace(); 166 | methods.put(methodName, null); 167 | loadedMethods.put(clazz, methods); 168 | return null; 169 | } 170 | } 171 | 172 | /** 173 | * Get a field with a particular name from a class 174 | * 175 | * @param clazz The class 176 | * @param fieldName The name of the field 177 | * @return The field object 178 | */ 179 | public static Field getField(Class> clazz, String fieldName) { 180 | if (!loadedFields.containsKey(clazz)) { 181 | loadedFields.put(clazz, new HashMap()); 182 | } 183 | 184 | Map fields = loadedFields.get(clazz); 185 | 186 | if (fields.containsKey(fieldName)) { 187 | return fields.get(fieldName); 188 | } 189 | 190 | try { 191 | Field field = clazz.getField(fieldName); 192 | fields.put(fieldName, field); 193 | loadedFields.put(clazz, fields); 194 | return field; 195 | } catch (Exception e) { 196 | e.printStackTrace(); 197 | fields.put(fieldName, null); 198 | loadedFields.put(clazz, fields); 199 | return null; 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bukkit/Config.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bukkit; 2 | 3 | import org.lintx.plugins.modules.configure.Configure; 4 | import org.lintx.plugins.modules.configure.YamlConfig; 5 | import org.lintx.plugins.yinwuchat.json.HandleConfig; 6 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 7 | 8 | import java.io.File; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | @YamlConfig 13 | public class Config { 14 | private static int version = 1; 15 | private static Config instance = new Config(); 16 | 17 | public static Config getInstance(){ 18 | return instance; 19 | } 20 | 21 | @YamlConfig 22 | List format = null; 23 | 24 | @YamlConfig 25 | List toFormat = null; 26 | 27 | @YamlConfig 28 | List fromFormat = null; 29 | 30 | @YamlConfig 31 | int eventDelayTime = 0; 32 | 33 | @YamlConfig 34 | List messageHandles = null; 35 | 36 | @YamlConfig 37 | private int configVersion = 0; 38 | 39 | private Config(){ 40 | 41 | } 42 | 43 | public void load(YinwuChat plugin){ 44 | Configure.bukkitLoad(plugin,this); 45 | if (format==null || format.isEmpty()){ 46 | format = new ArrayList<>(); 47 | format.add(new MessageFormat("&b[ServerName]","所在服务器:ServerName","/server ServerName")); 48 | format.add(new MessageFormat("&e{displayName}","点击私聊","/msg {displayName}")); 49 | format.add(new MessageFormat(" &6>>> ")); 50 | format.add(new MessageFormat("&r{message}")); 51 | } 52 | if (toFormat==null || toFormat.isEmpty()){ 53 | toFormat = new ArrayList<>(); 54 | toFormat.add(new MessageFormat("&7我 &6-> ")); 55 | toFormat.add(new MessageFormat("&e{displayName}","点击私聊","/msg {displayName}")); 56 | toFormat.add(new MessageFormat(" &6>>> ")); 57 | toFormat.add(new MessageFormat("&r{message}")); 58 | } 59 | if (fromFormat==null || fromFormat.isEmpty()){ 60 | fromFormat = new ArrayList<>(); 61 | fromFormat.add(new MessageFormat("&b[ServerName]","所在服务器:ServerName","/server ServerName")); 62 | fromFormat.add(new MessageFormat("&e{displayName}","点击私聊","/msg {displayName}")); 63 | fromFormat.add(new MessageFormat(" &6-> &7我")); 64 | fromFormat.add(new MessageFormat(" &6>>> ")); 65 | fromFormat.add(new MessageFormat("&r{message}")); 66 | } 67 | if (messageHandles==null) messageHandles = new ArrayList<>(); 68 | if (configVersion==0 && messageHandles.isEmpty()){ 69 | HandleConfig position = new HandleConfig(); 70 | position.placeholder = "\\[p\\]"; 71 | position.format = new ArrayList<>(); 72 | position.format.add(new MessageFormat("&7[位置]","所在服务器:ServerName\n所在世界:%player_world%\n坐标:X:%player_x% Y:%player_y% Z:%player_z%","")); 73 | messageHandles.add(position); 74 | } 75 | File file = new File(plugin.getDataFolder(),"config.yml"); 76 | if (!file.exists() || version!=configVersion){ 77 | configVersion = version; 78 | Configure.bukkitSave(plugin,this); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bukkit/Listeners.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bukkit; 2 | 3 | import com.google.common.io.ByteArrayDataInput; 4 | import com.google.common.io.ByteStreams; 5 | import com.google.gson.reflect.TypeToken; 6 | import org.bukkit.Sound; 7 | import org.bukkit.SoundCategory; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.EventPriority; 11 | import org.bukkit.event.Listener; 12 | import org.bukkit.event.player.AsyncPlayerChatEvent; 13 | import org.bukkit.plugin.messaging.PluginMessageListener; 14 | import org.lintx.plugins.yinwuchat.Const; 15 | import org.lintx.plugins.yinwuchat.Util.Gson; 16 | 17 | import java.util.List; 18 | 19 | public class Listeners implements Listener, PluginMessageListener { 20 | private final YinwuChat plugin; 21 | Listeners(YinwuChat plugin){ 22 | this.plugin = plugin; 23 | } 24 | 25 | @EventHandler(priority = EventPriority.MONITOR,ignoreCancelled = true) 26 | public void onChat(AsyncPlayerChatEvent event){ 27 | if (event.isAsynchronous() && Config.getInstance().eventDelayTime>0){ 28 | try { 29 | Thread.sleep(Config.getInstance().eventDelayTime); 30 | } catch (InterruptedException ignored) { 31 | 32 | } 33 | } 34 | if (event.isCancelled()) return; 35 | Player player = event.getPlayer(); 36 | String chat = event.getMessage(); 37 | 38 | MessageManage.getInstance().onPublicMessage(player,chat); 39 | 40 | event.setCancelled(true); 41 | } 42 | 43 | @Override 44 | public void onPluginMessageReceived(String channel, Player player, byte[] bytes) { 45 | if (!Const.PLUGIN_CHANNEL.equals(channel)){ 46 | return; 47 | } 48 | ByteArrayDataInput input = ByteStreams.newDataInput(bytes); 49 | String subchannel = input.readUTF(); 50 | if (Const.PLUGIN_SUB_CHANNEL_AT.equals(subchannel)){ 51 | player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, SoundCategory.PLAYERS,1.0f,1.0f); 52 | } 53 | else if (Const.PLUGIN_SUB_CHANNEL_PLAYER_LIST.equals(subchannel)){ 54 | try { 55 | plugin.bungeePlayerList = Gson.gson().fromJson(input.readUTF(),new TypeToken>(){}.getType()); 56 | }catch (Exception ignored){ 57 | 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bukkit/MessageManage.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bukkit; 2 | 3 | import com.google.common.io.ByteArrayDataOutput; 4 | import com.google.common.io.ByteStreams; 5 | import com.google.gson.Gson; 6 | import me.clip.placeholderapi.PlaceholderAPI; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.bukkit.inventory.PlayerInventory; 11 | import org.lintx.plugins.yinwuchat.Const; 12 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 13 | import org.lintx.plugins.yinwuchat.json.*; 14 | import org.lintx.plugins.yinwuchat.Util.ItemUtil; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | 21 | public class MessageManage { 22 | private static MessageManage instance = new MessageManage(); 23 | private YinwuChat plugin; 24 | private MessageManage(){} 25 | void setPlugin(YinwuChat plugin){ 26 | this.plugin = plugin; 27 | } 28 | public static MessageManage getInstance(){ 29 | return instance; 30 | } 31 | 32 | private String filterStyle(Player player,String chat){ 33 | String permissions = "0123456789abcdefklmnor"; 34 | StringBuilder deny = new StringBuilder(); 35 | for (int i=0;i format(Player player, List formats){ 101 | boolean hasPAPI = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null && 102 | Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"); 103 | List list = new ArrayList<>(); 104 | for (MessageFormat message:formats){ 105 | if (message.message==null || message.message.equals("")) continue; 106 | 107 | String msg = message.message; 108 | msg = papi(hasPAPI,msg,player); 109 | 110 | MessageFormat format = new MessageFormat(msg); 111 | if (message.hover!=null){ 112 | format.hover = papi(hasPAPI,message.hover,player); 113 | } 114 | if (message.click!=null){ 115 | format.click = papi(hasPAPI,message.click,player); 116 | } 117 | list.add(format); 118 | } 119 | return list; 120 | } 121 | 122 | private String papi(boolean open,String string,Player player){ 123 | if (!open){ 124 | return string; 125 | } 126 | try { 127 | return PlaceholderAPI.setPlaceholders(player,string); 128 | }catch (Exception ignored){ 129 | return string; 130 | } 131 | } 132 | 133 | private List getMessageItems(String message,Player player){ 134 | Pattern pattern = Pattern.compile(Const.ITEM_PLACEHOLDER); 135 | Matcher matcher = pattern.matcher(message); 136 | List list = new ArrayList<>(); 137 | PlayerInventory inventory = player.getInventory(); 138 | while (matcher.find()){ 139 | int index = -1; 140 | String s = matcher.group(2); 141 | try { 142 | index = Integer.parseInt(s); 143 | if (index>40 || index<0){ 144 | index = -1; 145 | } 146 | }catch (Exception ignored){ } 147 | ItemStack itemStack; 148 | if (index==-1){ 149 | itemStack = inventory.getItemInMainHand()==null?player.getInventory().getItemInOffHand():player.getInventory().getItemInMainHand(); 150 | }else { 151 | itemStack = inventory.getItem(index); 152 | } 153 | 154 | list.add(ItemUtil.itemJsonWithPlayer(itemStack)); 155 | } 156 | return list; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bukkit/YinwuChat.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bukkit; 2 | 3 | import com.google.common.io.ByteArrayDataOutput; 4 | import com.google.common.io.ByteStreams; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | import org.lintx.plugins.yinwuchat.Const; 8 | import org.lintx.plugins.yinwuchat.bukkit.commands.PrivateMessage; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.List; 13 | 14 | public class YinwuChat extends JavaPlugin { 15 | public List bungeePlayerList = new ArrayList<>(); 16 | 17 | @Override 18 | public void onEnable() { 19 | Config.getInstance().load(this); 20 | MessageManage.getInstance().setPlugin(this); 21 | Listeners listeners = new Listeners(this); 22 | getServer().getMessenger().registerOutgoingPluginChannel(this, Const.PLUGIN_CHANNEL); 23 | getServer().getMessenger().registerIncomingPluginChannel(this,Const.PLUGIN_CHANNEL,listeners); 24 | getServer().getPluginManager().registerEvents(listeners,this); 25 | getCommand("msg").setExecutor(new PrivateMessage(this)); 26 | getCommand("yinwuchat-bukkit").setExecutor(new org.lintx.plugins.yinwuchat.bukkit.commands.YinwuChat(this)); 27 | 28 | requirePlayerList(); 29 | } 30 | 31 | @Override 32 | public void onDisable() { 33 | getServer().getMessenger().unregisterIncomingPluginChannel(this); 34 | getServer().getMessenger().unregisterOutgoingPluginChannel(this); 35 | } 36 | 37 | private void requirePlayerList(){ 38 | Collection extends Player> players = getServer().getOnlinePlayers(); 39 | if (players==null || players.isEmpty() || !players.iterator().hasNext()) return; 40 | Player player = players.iterator().next(); 41 | if (player==null) return; 42 | ByteArrayDataOutput output = ByteStreams.newDataOutput(); 43 | output.writeUTF(Const.PLUGIN_SUB_CHANNEL_PLAYER_LIST); 44 | player.sendPluginMessage(this,Const.PLUGIN_CHANNEL,output.toByteArray()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bukkit/commands/PrivateMessage.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bukkit.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.command.Command; 5 | import org.bukkit.command.CommandExecutor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.command.TabExecutor; 8 | import org.bukkit.entity.Player; 9 | import org.lintx.plugins.yinwuchat.bukkit.MessageManage; 10 | import org.lintx.plugins.yinwuchat.bukkit.YinwuChat; 11 | 12 | import java.util.*; 13 | 14 | public class PrivateMessage implements CommandExecutor, TabExecutor { 15 | private final YinwuChat plugin; 16 | public PrivateMessage(YinwuChat plugin){ 17 | this.plugin = plugin; 18 | } 19 | 20 | @Override 21 | public boolean onCommand(CommandSender commandSender, Command command, String labelName, String[] args) { 22 | if (!(commandSender instanceof Player)){ 23 | commandSender.sendMessage(ChatColor.RED +"Must use command in-game"); 24 | return true; 25 | } 26 | Player player = (Player)commandSender; 27 | if (args.length>=2) { 28 | String to_player_name = args[0]; 29 | List tmpList = new ArrayList<>(Arrays.asList(args).subList(1, args.length)); 30 | String msg = String.join(" ", tmpList); 31 | 32 | MessageManage.getInstance().onPrivateMessage(player,to_player_name,msg); 33 | return true; 34 | } 35 | else{ 36 | commandSender.sendMessage(ChatColor.RED + "命令格式:/msg 玩家名 消息"); 37 | commandSender.sendMessage(ChatColor.RED + "缺少玩家名或消息"); 38 | } 39 | return true; 40 | } 41 | 42 | @Override 43 | public List onTabComplete(CommandSender commandSender, Command command, String alias, String[] args) { 44 | List players = new ArrayList<>(plugin.bungeePlayerList); 45 | if (args.length>0){ 46 | String name = args[0]; 47 | String n = name.toLowerCase(Locale.ROOT); 48 | players.removeIf(s -> !s.toLowerCase(Locale.ROOT).contains(n)); //移除无相关性的玩家 49 | } 50 | return players; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bukkit/commands/YinwuChat.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bukkit.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.command.Command; 5 | import org.bukkit.command.CommandExecutor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.command.TabExecutor; 8 | import org.bukkit.entity.Player; 9 | import org.lintx.plugins.yinwuchat.bukkit.Config; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class YinwuChat implements CommandExecutor, TabExecutor { 15 | private final org.lintx.plugins.yinwuchat.bukkit.YinwuChat plugin; 16 | public YinwuChat(org.lintx.plugins.yinwuchat.bukkit.YinwuChat plugin){ 17 | this.plugin = plugin; 18 | } 19 | 20 | @Override 21 | public boolean onCommand(CommandSender commandSender, Command command, String labelName, String[] args) { 22 | if (args.length>=1){ 23 | String arg1 = args[0]; 24 | if (arg1.equalsIgnoreCase("reload")){ 25 | if (commandSender instanceof Player){ 26 | if (!commandSender.hasPermission("yinwuchat.reload")){ 27 | commandSender.sendMessage(ChatColor.RED + "权限不足"); 28 | return true; 29 | } 30 | } 31 | Config.getInstance().load(plugin); 32 | commandSender.sendMessage(ChatColor.GREEN + "插件配置重新加载成功"); 33 | return true; 34 | } 35 | } 36 | commandSender.sendMessage(ChatColor.RED + "命令格式:/yinwuchat-bukkit reload"); 37 | return true; 38 | } 39 | 40 | @Override 41 | public List onTabComplete(CommandSender commandSender, Command command, String alias, String[] args) { 42 | List list = new ArrayList<>(); 43 | list.add("reload"); 44 | return list; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/BatManage.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee; 2 | 3 | import fr.Alphart.BAT.BAT; 4 | import net.md_5.bungee.api.connection.ProxiedPlayer; 5 | 6 | class BatManage { 7 | private final YinwuChat plugin; 8 | private boolean hasBAT; 9 | private BAT bat; 10 | 11 | BatManage(YinwuChat plugin){ 12 | this.plugin = plugin; 13 | 14 | } 15 | 16 | private void checkBat(){ 17 | hasBAT = plugin.getProxy().getPluginManager().getPlugin("BungeeAdminTools") != null; 18 | if (hasBAT){ 19 | try { 20 | bat = BAT.getInstance(); 21 | if (bat==null){ 22 | hasBAT = false; 23 | } 24 | } 25 | catch (Exception e){ 26 | hasBAT = false; 27 | } 28 | } 29 | } 30 | 31 | boolean isBan(ProxiedPlayer player,String server){ 32 | checkBat(); 33 | if (!hasBAT) return false; 34 | try { 35 | return bat.getModules().getBanModule().isBan(player,server); 36 | } 37 | catch (Exception e){ 38 | return false; 39 | } 40 | } 41 | 42 | boolean isBan(String player,String server){ 43 | checkBat(); 44 | if (!hasBAT) return false; 45 | try { 46 | return bat.getModules().getBanModule().isBan(player,server); 47 | } 48 | catch (Exception e){ 49 | return false; 50 | } 51 | } 52 | 53 | boolean isMute(ProxiedPlayer player,String server){ 54 | if (!hasBAT) return false; 55 | try { 56 | return bat.getModules().getMuteModule().isMute(player,server) > 0; 57 | } 58 | catch (Exception e){ 59 | return false; 60 | } 61 | } 62 | 63 | boolean isMute(String player,String server){ 64 | if (!hasBAT) return false; 65 | try { 66 | return bat.getModules().getMuteModule().isMute(player,server,true); 67 | } 68 | catch (Exception e){ 69 | return false; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/IgnoreCommand.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee; 2 | 3 | import net.md_5.bungee.api.ChatColor; 4 | import net.md_5.bungee.api.CommandSender; 5 | import net.md_5.bungee.api.connection.ProxiedPlayer; 6 | import net.md_5.bungee.api.plugin.Command; 7 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 8 | import org.lintx.plugins.yinwuchat.bungee.config.PlayerConfig; 9 | import org.lintx.plugins.yinwuchat.bungee.httpserver.WsClientHelper; 10 | import org.lintx.plugins.yinwuchat.bungee.httpserver.WsClientUtil; 11 | 12 | import java.util.Locale; 13 | 14 | public class IgnoreCommand extends Command { 15 | private final YinwuChat plugin; 16 | public IgnoreCommand(YinwuChat plugin,String name) { 17 | super(name); 18 | this.plugin = plugin; 19 | } 20 | 21 | @Override 22 | public void execute(CommandSender sender, String[] args) { 23 | if (!(sender instanceof ProxiedPlayer)) { 24 | sender.sendMessage(MessageUtil.newTextComponent("Must use command in-game")); 25 | return; 26 | } 27 | final ProxiedPlayer player = (ProxiedPlayer)sender; 28 | if (args.length>=1) { 29 | PlayerConfig.Player playerConfig = PlayerConfig.getConfig(player); 30 | String name = args[0].toLowerCase(Locale.ROOT); 31 | for (ProxiedPlayer p:plugin.getProxy().getPlayers()){ 32 | if (p.getName().toLowerCase(Locale.ROOT).equals(name)){ 33 | if (playerConfig.ignore(p.getUniqueId())){ 34 | sender.sendMessage(MessageUtil.newTextComponent(ChatColor.RED + "你现在忽略了"+p.getName()+"的信息")); 35 | } 36 | else { 37 | sender.sendMessage(MessageUtil.newTextComponent(ChatColor.GREEN + "你不再忽略"+p.getName()+"的信息")); 38 | } 39 | return; 40 | } 41 | } 42 | 43 | for (WsClientUtil util: WsClientHelper.utils()){ 44 | PlayerConfig.Player p = PlayerConfig.getConfig(util.getUuid()); 45 | if (name.equals(p.name.toLowerCase(Locale.ROOT))){ 46 | if (playerConfig.ignore(util.getUuid())){ 47 | sender.sendMessage(MessageUtil.newTextComponent(ChatColor.RED + "你现在忽略了"+p.name+"的信息")); 48 | } 49 | else { 50 | sender.sendMessage(MessageUtil.newTextComponent(ChatColor.GREEN + "你不再忽略"+p.name+"的信息")); 51 | } 52 | return; 53 | } 54 | } 55 | } 56 | sender.sendMessage(MessageUtil.newTextComponent(ChatColor.GOLD + "忽略某人的消息:/yinwuchat ignore (再输入一次不再忽略)")); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/Listeners.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee; 2 | 3 | import com.google.common.io.ByteArrayDataInput; 4 | import com.google.common.io.ByteStreams; 5 | import net.md_5.bungee.api.connection.ProxiedPlayer; 6 | import net.md_5.bungee.api.connection.Server; 7 | import net.md_5.bungee.api.event.*; 8 | import net.md_5.bungee.api.plugin.Listener; 9 | import net.md_5.bungee.event.EventHandler; 10 | import org.lintx.plugins.yinwuchat.Const; 11 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 12 | import org.lintx.plugins.yinwuchat.bungee.config.PlayerConfig; 13 | import org.lintx.plugins.yinwuchat.bungee.json.OutputPlayerList; 14 | 15 | public class Listeners implements Listener { 16 | private final YinwuChat plugin; 17 | private Config config = Config.getInstance(); 18 | 19 | Listeners(YinwuChat plugin){ 20 | this.plugin = plugin; 21 | } 22 | 23 | @EventHandler 24 | public void onPluginMessage(PluginMessageEvent event){ 25 | plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { 26 | @Override 27 | public void run() { 28 | if (event.getTag().equals(Const.PLUGIN_CHANNEL)){ 29 | if (event.getReceiver() instanceof ProxiedPlayer && event.getSender() instanceof Server){ 30 | ProxiedPlayer player = (ProxiedPlayer)event.getReceiver(); 31 | ByteArrayDataInput input = ByteStreams.newDataInput(event.getData()); 32 | MessageManage.getInstance().handleBukkitMessage(player,input); 33 | } 34 | } 35 | } 36 | }); 37 | } 38 | 39 | @EventHandler 40 | public void onPostLogin(PostLoginEvent event){ 41 | if (event.getPlayer() != null) { 42 | PlayerConfig.getConfig(event.getPlayer()); 43 | } 44 | OutputPlayerList.sendGamePlayerList(); 45 | MessageManage.getInstance().sendPlayerListToServer(); 46 | if (config.redisConfig.openRedis){ 47 | RedisUtil.sendPlayerList(); 48 | } 49 | } 50 | 51 | @EventHandler 52 | public void onPlayerDisconnect(PlayerDisconnectEvent event){ 53 | if (event.getPlayer() != null) { 54 | PlayerConfig.unloadConfig(event.getPlayer()); 55 | } 56 | OutputPlayerList.sendGamePlayerList(); 57 | MessageManage.getInstance().sendPlayerListToServer(); 58 | if (config.redisConfig.openRedis){ 59 | RedisUtil.sendPlayerList(); 60 | } 61 | } 62 | 63 | @EventHandler 64 | public void onServerConnected(ServerConnectedEvent event){ 65 | MessageManage.getInstance().sendPlayerListToServer(event.getServer()); 66 | } 67 | 68 | @EventHandler 69 | public void onServerSwitch(ServerSwitchEvent event){ 70 | MessageManage.getInstance().sendPlayerListToServer(event.getPlayer().getServer()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/RedisUtil.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee; 2 | 3 | import com.google.common.io.ByteArrayDataOutput; 4 | import com.google.common.io.ByteStreams; 5 | import com.google.gson.JsonObject; 6 | import io.netty.channel.Channel; 7 | import net.md_5.bungee.api.chat.BaseComponent; 8 | import net.md_5.bungee.api.chat.TextComponent; 9 | import net.md_5.bungee.api.connection.ProxiedPlayer; 10 | import net.md_5.bungee.chat.ComponentSerializer; 11 | import org.lintx.plugins.yinwuchat.Const; 12 | import org.lintx.plugins.yinwuchat.Util.Gson; 13 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 14 | import org.lintx.plugins.yinwuchat.bungee.config.PlayerConfig; 15 | import org.lintx.plugins.yinwuchat.bungee.config.RedisConfig; 16 | import org.lintx.plugins.yinwuchat.bungee.httpserver.NettyChannelMessageHelper; 17 | import org.lintx.plugins.yinwuchat.bungee.httpserver.WsClientHelper; 18 | import org.lintx.plugins.yinwuchat.bungee.json.OutputCoolQ; 19 | import org.lintx.plugins.yinwuchat.bungee.json.RedisMessage; 20 | import org.lintx.plugins.yinwuchat.bungee.json.RedisMessageType; 21 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 22 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 23 | import redis.clients.jedis.Jedis; 24 | import redis.clients.jedis.JedisPool; 25 | import redis.clients.jedis.JedisPoolConfig; 26 | import redis.clients.jedis.JedisPubSub; 27 | 28 | import java.util.*; 29 | 30 | public class RedisUtil { 31 | private static final String REDIS_SUBSCRIBE_CHANNEL = "yinwuchat-redis-subscribe-channel"; 32 | private static JedisPool jedisPool; 33 | private static Subscribe subscribe; 34 | private static YinwuChat plugin; 35 | public static Map playerList = new HashMap<>();//player-server 36 | 37 | static void init(YinwuChat plugin){ 38 | RedisUtil.plugin = plugin; 39 | RedisConfig config = Config.getInstance().redisConfig; 40 | if (!config.openRedis) return; 41 | if (jedisPool != null || subscribe != null){ 42 | unload(); 43 | } 44 | JedisPoolConfig poolConfig = new JedisPoolConfig(); 45 | poolConfig.setMaxTotal(Config.getInstance().redisConfig.maxConnection); 46 | String password = config.password; 47 | if (password!=null && password.isEmpty()) password = null; 48 | jedisPool = new JedisPool(poolConfig,config.ip,config.port,0,password); 49 | subscribe = new Subscribe(); 50 | 51 | plugin.getProxy().getScheduler().runAsync(plugin,()->{ 52 | try (Jedis jedis = jedisPool.getResource()) { 53 | jedis.subscribe(subscribe,REDIS_SUBSCRIBE_CHANNEL); 54 | }catch (Exception ignored){} 55 | }); 56 | } 57 | 58 | static void unload(){ 59 | try { 60 | subscribe.unsubscribe(); 61 | }catch (Exception ignored){} 62 | subscribe = null; 63 | jedisPool = null; 64 | } 65 | 66 | private static class Subscribe extends JedisPubSub { 67 | @Override 68 | public void onMessage(String channel, String message) { 69 | plugin.getProxy().getScheduler().runAsync(plugin,()->{ 70 | // String messageId = UUID.randomUUID().toString(); 71 | // long time1 = System.nanoTime(); 72 | // plugin.getLogger().info("Start processing redis messages[" + messageId + "],nanoTime:" + time1 + ",message:" + message); 73 | if (channel.equals(REDIS_SUBSCRIBE_CHANNEL)){ 74 | try { 75 | RedisMessage obj = Gson.gson().fromJson(message,RedisMessage.class); 76 | 77 | RedisUtil.onMessage(obj); 78 | }catch (Exception ignored){ 79 | ignored.printStackTrace(); 80 | } 81 | } 82 | // long time2 = System.nanoTime(); 83 | // long usageTime = time2 - time1; 84 | // plugin.getLogger().info("redis message processed[" + messageId + "],nanoTime:" + time2 + ",usage time:" + usageTime); 85 | }); 86 | } 87 | } 88 | 89 | private static void onMessage(RedisMessage message){ 90 | RedisConfig config = Config.getInstance().redisConfig; 91 | //自己服务器发送的消息不执行后续动作 92 | if (message.fromServer.equals(config.selfName)){ 93 | return; 94 | } 95 | 96 | //对单个服务器发送的消息,如果目标不是自己,则不执行后续动作 97 | if (!"".equals(message.toServer) && !message.toServer.equals(config.selfName)){ 98 | return; 99 | } 100 | 101 | ProxiedPlayer player = null; 102 | PlayerConfig.Player pc = null; 103 | 104 | //对单个玩家发送的消息的前期处理 105 | if (!"".equals(message.toPlayerName)){ 106 | player = YinwuChat.getPlugin().getProxy().getPlayer(message.toPlayerName); 107 | if (player==null) return; 108 | pc = PlayerConfig.getConfig(player); 109 | //如果该玩家忽略了发送消息的玩家的消息,则退出 110 | if (pc.isIgnore(message.fromPlayerUUID)) return; 111 | } 112 | 113 | if (!"".equals(message.message)) message.chat = ComponentSerializer.parse(message.message)[0]; 114 | 115 | switch (message.type){ 116 | case AT_PLAYER: 117 | //AT单个玩家 118 | if (!config.forwardBcAtOne) break; 119 | if (player==null) break; 120 | if (pc.banAt) break; 121 | player.sendMessage(message.chat); 122 | 123 | if (pc.muteAt) break; 124 | ByteArrayDataOutput output = ByteStreams.newDataOutput(); 125 | output.writeUTF(Const.PLUGIN_SUB_CHANNEL_AT); 126 | player.getServer().sendData(Const.PLUGIN_CHANNEL,output.toByteArray()); 127 | break; 128 | case AT_PLAYER_ALL: 129 | //AT 所有人 130 | if (!config.forwardBcAtAll) break; 131 | ByteArrayDataOutput output1 = ByteStreams.newDataOutput(); 132 | output1.writeUTF(Const.PLUGIN_SUB_CHANNEL_AT); 133 | for (ProxiedPlayer p : YinwuChat.getPlugin().getProxy().getPlayers()){ 134 | p.sendMessage(message.chat); 135 | p.getServer().sendData(Const.PLUGIN_CHANNEL,output1.toByteArray()); 136 | } 137 | break; 138 | case PUBLIC_MESSAGE: 139 | case TASK: 140 | //公屏消息 141 | if (message.type==RedisMessageType.TASK && !config.forwardBcTask) break; 142 | for (ProxiedPlayer p : YinwuChat.getPlugin().getProxy().getPlayers()){ 143 | if (!"".equals(message.toMCServer)){ 144 | if (!p.getServer().getInfo().getName().equalsIgnoreCase(message.toMCServer)){ 145 | continue; 146 | } 147 | } 148 | PlayerConfig.Player pc2 = PlayerConfig.getConfig(p); 149 | if (pc2.isIgnore(message.fromPlayerUUID)) continue; 150 | p.sendMessage(message.chat); 151 | } 152 | if (plugin.wsIsOn() && config.forwardBcMessageToWeb){ 153 | String webmessage = message.chat.toLegacyText(); 154 | JsonObject webjson = new JsonObject(); 155 | webjson.addProperty("action", "send_message"); 156 | webjson.addProperty("message", webmessage); 157 | String json = Gson.gson().toJson(webjson); 158 | 159 | for (Channel channel : WsClientHelper.channels()) { 160 | NettyChannelMessageHelper.send(channel,json); 161 | } 162 | } 163 | if (Config.getInstance().coolQConfig.coolQGameToQQ && config.forwardBcMessageToQQ){ 164 | Channel channel = WsClientHelper.getCoolQ(); 165 | if (channel!=null){ 166 | String qqmessage = message.chat.toPlainText(); 167 | qqmessage = qqmessage.replaceAll("§([0-9a-fklmnor])",""); 168 | try { 169 | NettyChannelMessageHelper.send(channel,new OutputCoolQ(qqmessage).getJSON()); 170 | }catch (Exception e){ 171 | e.printStackTrace(); 172 | } 173 | } 174 | } 175 | break; 176 | case PRIVATE_MESSAGE: 177 | //私聊消息 178 | if (player==null) return; 179 | player.sendMessage(message.chat); 180 | break; 181 | case PLAYER_LIST: 182 | //玩家列表 183 | Collection col = playerList.values(); 184 | while (col.contains(message.fromServer)){ 185 | col.remove(message.fromServer); 186 | } 187 | for (String playerName:message.playerList){ 188 | playerList.put(playerName,message.fromServer); 189 | } 190 | MessageManage.getInstance().sendPlayerListToServer(); 191 | break; 192 | } 193 | } 194 | 195 | public static void sendMessage( UUID uuid,BaseComponent chat){ 196 | sendMessage(RedisMessageType.PUBLIC_MESSAGE,uuid,chat,""); 197 | } 198 | 199 | public static void sendMessage( UUID uuid,BaseComponent chat,String toPlayer){ 200 | sendMessage(RedisMessageType.PRIVATE_MESSAGE,uuid,chat,toPlayer); 201 | } 202 | 203 | public static void sendMessage(RedisMessageType type, UUID uuid, BaseComponent chat, String toPlayer){ 204 | sendMessage(type,uuid,chat,toPlayer,""); 205 | } 206 | 207 | public static void sendMessage(RedisMessageType type, UUID uuid, BaseComponent chat, String toPlayer, String mcServer){ 208 | Chat prefix = new Chat(); 209 | TextComponent messageComponent = new TextComponent(); 210 | List formats = Config.getInstance().redisConfig.selfPrefixFormat; 211 | if (formats!=null && !formats.isEmpty()){ 212 | for (MessageFormat format:Config.getInstance().redisConfig.selfPrefixFormat){ 213 | if (format.message==null || format.message.equals("")) continue; 214 | messageComponent.addExtra(prefix.buildFormat(format)); 215 | } 216 | } 217 | messageComponent.addExtra(chat); 218 | 219 | RedisMessage message = new RedisMessage(); 220 | // message.chat = chat; 221 | message.toMCServer = mcServer; 222 | message.message = ComponentSerializer.toString(messageComponent); 223 | message.toPlayerName = toPlayer; 224 | message.fromServer = Config.getInstance().redisConfig.selfName; 225 | message.type = type; 226 | message.fromPlayerUUID = uuid; 227 | if (!"".equals(toPlayer)){ 228 | String toServer = playerList.get(toPlayer); 229 | if (toServer!=null) message.toServer = toServer; 230 | } 231 | sendMessage(message); 232 | } 233 | 234 | public static void sendPlayerList(){ 235 | RedisMessage message = new RedisMessage(); 236 | message.type = RedisMessageType.PLAYER_LIST; 237 | message.fromServer = Config.getInstance().redisConfig.selfName; 238 | for (ProxiedPlayer p : YinwuChat.getPlugin().getProxy().getPlayers()){ 239 | message.playerList.add(p.getName()); 240 | } 241 | sendMessage(message); 242 | } 243 | 244 | private static void sendMessage(RedisMessage message){ 245 | if (jedisPool==null){ 246 | return; 247 | } 248 | plugin.getProxy().getScheduler().runAsync(plugin, () -> { 249 | try (Jedis jedis = jedisPool.getResource()) { 250 | jedis.publish(REDIS_SUBSCRIBE_CHANNEL, Gson.gson().toJson(message)); 251 | } 252 | }); 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/ShieldedManage.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee; 2 | 3 | 4 | import io.netty.channel.Channel; 5 | import net.md_5.bungee.api.connection.ProxiedPlayer; 6 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 7 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 8 | import org.lintx.plugins.yinwuchat.bungee.json.OutputServerMessage; 9 | import org.lintx.plugins.yinwuchat.bungee.httpserver.NettyChannelMessageHelper; 10 | 11 | import java.time.Duration; 12 | import java.time.LocalDateTime; 13 | import java.util.HashMap; 14 | import java.util.Locale; 15 | import java.util.Map; 16 | 17 | public class ShieldedManage { 18 | private static final ShieldedManage instance = new ShieldedManage(); 19 | public static ShieldedManage getInstance(){ 20 | return instance; 21 | } 22 | 23 | private Map users = new HashMap<>(); 24 | 25 | private static class Man { 26 | int count = 0; 27 | LocalDateTime first = null; 28 | } 29 | 30 | private String formatMessage(String string){ 31 | string = string.replaceAll("&([0-9a-fklmnor])","§$1"); 32 | return string; 33 | } 34 | 35 | Result checkShielded(Channel channel, String uuid, String message){ 36 | Result result = checkShielded(uuid,message); 37 | if (result.kick){ 38 | NettyChannelMessageHelper.send(channel, OutputServerMessage.errorJSON(formatMessage(Config.getInstance().tipsConfig.shieldedKickTip)).getJSON()); 39 | channel.close(); 40 | } 41 | else if (result.shielded){ 42 | NettyChannelMessageHelper.send(channel, OutputServerMessage.errorJSON(formatMessage(Config.getInstance().tipsConfig.shieldedTip)).getJSON()); 43 | } 44 | return result; 45 | } 46 | 47 | Result checkShielded(ProxiedPlayer player,String message){ 48 | Result result = checkShielded(player.getUniqueId().toString(),message); 49 | if (result.kick){ 50 | player.disconnect(MessageUtil.newTextComponent(formatMessage(Config.getInstance().tipsConfig.shieldedKickTip))); 51 | } 52 | else if (result.shielded){ 53 | player.sendMessage(MessageUtil.newTextComponent(formatMessage(Config.getInstance().tipsConfig.shieldedTip))); 54 | } 55 | return result; 56 | } 57 | 58 | private Result checkShielded(String uuid,String message){ 59 | String string = message.replaceAll("&([0-9a-fklmnor])","").replaceAll(" ","").toLowerCase(Locale.ROOT); 60 | Result result = new Result(); 61 | Config config = Config.getInstance(); 62 | if (config.shieldeds.parallelStream().anyMatch(string::contains)){ 63 | YinwuChat.getPlugin().getLogger().info(uuid + " send a shielded word: " + message); 64 | result.shielded = true; 65 | if (!users.containsKey(uuid)){ 66 | Man man = new Man(); 67 | man.first = LocalDateTime.now(); 68 | users.put(uuid,man); 69 | } 70 | 71 | Man man = users.get(uuid); 72 | Duration duration = Duration.between(man.first,LocalDateTime.now()); 73 | if (duration.toMillis()>config.shieldedKickTime*1000){ 74 | man.first = LocalDateTime.now(); 75 | man.count = 0; 76 | } 77 | man.count += 1; 78 | users.put(uuid,man); 79 | if (man.count>=config.shieldedKickCount){ 80 | result.kick = true; 81 | return result; 82 | } 83 | 84 | if (config.shieldedMode==1){ 85 | result.msg = config.tipsConfig.shieldedReplace; 86 | } 87 | else { 88 | result.end = true; 89 | } 90 | } 91 | return result; 92 | } 93 | 94 | static class Result{ 95 | boolean shielded = false; 96 | boolean end = false; 97 | boolean kick = false; 98 | String msg = ""; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/YinwuChat.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee; 2 | 3 | import net.md_5.bungee.api.ChatColor; 4 | import net.md_5.bungee.api.plugin.Plugin; 5 | import net.md_5.bungee.api.scheduler.ScheduledTask; 6 | import org.bstats.bungeecord.Metrics; 7 | import org.lintx.plugins.yinwuchat.Const; 8 | import org.lintx.plugins.yinwuchat.bungee.announcement.Task; 9 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 10 | import org.lintx.plugins.yinwuchat.bungee.httpserver.WsClientHelper; 11 | import org.lintx.plugins.yinwuchat.bungee.httpserver.NettyHttpServer; 12 | 13 | import java.io.File; 14 | import java.io.FileOutputStream; 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.net.ServerSocket; 18 | import java.util.Enumeration; 19 | import java.util.concurrent.TimeUnit; 20 | import java.util.zip.ZipEntry; 21 | import java.util.zip.ZipFile; 22 | 23 | public class YinwuChat extends Plugin { 24 | private static YinwuChat plugin; 25 | private static NettyHttpServer server = null; 26 | private static BatManage batManage; 27 | private ScheduledTask scheduledTask = null; 28 | private Config config = Config.getInstance(); 29 | public static NettyHttpServer getWSServer(){ 30 | return server; 31 | } 32 | 33 | public static YinwuChat getPlugin(){ 34 | return plugin; 35 | } 36 | 37 | static BatManage getBatManage(){ 38 | return batManage; 39 | } 40 | 41 | void reload(){ 42 | onDisable(); 43 | onEnable(); 44 | } 45 | 46 | void reloadConfig(){ 47 | //重载前是否开启了wsserver 48 | boolean wsopen = config.openwsserver; 49 | //重载前wsserver port 50 | int wsport = config.wsport; 51 | //重新加载配置 52 | config.load(this); 53 | //重载前已开启wsserver且重载后关闭wsserver时关闭wsserver 54 | if (wsopen && !config.openwsserver){ 55 | stopWsServer(); 56 | } 57 | //重载后开启wsserver且(重载前关闭wsserver或重载前port和重载后port不一致或wsserver未能成功开启)时启动wsserver 58 | if (config.openwsserver && (!wsopen || wsport!=config.wsport || server==null)){ 59 | startWs(); 60 | } 61 | //重新加载task 62 | org.lintx.plugins.yinwuchat.bungee.announcement.Config.getInstance().load(plugin); 63 | redisBungee(); 64 | } 65 | 66 | boolean wsIsOn(){ 67 | return config.openwsserver && server!=null; 68 | } 69 | 70 | @Override 71 | public void onEnable() { 72 | // System.out.println("test show item start"); 73 | // try { 74 | // String json = "{\"extra\":[{\"text\":\"§r§7[§r\"},{\"extra\":[{\"translate\":\"block.minecraft.dirt\"}],\"text\":\"\"},{\"text\":\"§r§7]§r\"}],\"hoverEvent\":{\"action\":\"show_item\",\"value\":[{\"text\":\"{id:\\\"minecraft:dirt\\\",Count:1b}\"}]},\"text\":\"\"}"; 75 | // System.out.println(json); 76 | // BaseComponent[] component = ComponentSerializer.parse(json); 77 | // if (component.length>0){ 78 | // System.out.println(component[0].toLegacyText()); 79 | // } 80 | // }catch (Exception e){ 81 | // e.printStackTrace(); 82 | // } 83 | // System.out.println("test show item end"); 84 | autoFreedWeb(); 85 | plugin = this; 86 | batManage = new BatManage(this); 87 | config.load(this); 88 | if (config.openwsserver){ 89 | startWs(); 90 | } 91 | MessageManage.setPlugin(this); 92 | getProxy().registerChannel(Const.PLUGIN_CHANNEL); 93 | getProxy().getPluginManager().registerListener(this,new Listeners(this)); 94 | getProxy().getPluginManager().registerCommand(this, new Commands(plugin,"yinwuchat")); 95 | getProxy().getPluginManager().registerCommand(this,new IgnoreCommand(this,"ignore")); 96 | 97 | org.lintx.plugins.yinwuchat.bungee.announcement.Config.getInstance().load(this); 98 | Task task = new Task(); 99 | scheduledTask = getProxy().getScheduler().schedule(this,task, 0L,1L, TimeUnit.SECONDS); 100 | 101 | MessageManage.getInstance().sendPlayerListToServer(); 102 | 103 | redisBungee(); 104 | 105 | Metrics metrics = new Metrics(this); 106 | } 107 | 108 | private void redisBungee(){ 109 | if (config.redisConfig.openRedis){ 110 | RedisUtil.init(this); 111 | } 112 | } 113 | 114 | @Override 115 | public void onDisable() { 116 | if (scheduledTask!=null){ 117 | scheduledTask.cancel(); 118 | } 119 | stopWsServer(); 120 | getProxy().unregisterChannel(Const.PLUGIN_CHANNEL); 121 | getProxy().getPluginManager().unregisterListeners(this); 122 | getProxy().getPluginManager().unregisterCommands(this); 123 | RedisUtil.unload(); 124 | } 125 | 126 | void startWs(){ 127 | stopWsServer(); 128 | 129 | int port = config.wsport; 130 | if (!isPortAvailable(port)) { 131 | getLogger().info(ChatColor.RED+"端口"+port+"被占用,无法开启WebSocket服务,请检查端口绑定情况,或稍后再试,或修改WebSocket端口!"); 132 | return; 133 | } 134 | try { 135 | server = new NettyHttpServer(config.wsport,this,new File(getDataFolder(),"web")); 136 | getProxy().getScheduler().runAsync(this, () -> server.start()); 137 | } catch (Exception e) { 138 | e.printStackTrace(); 139 | } 140 | } 141 | 142 | private void stopWsServer(){ 143 | try { 144 | if (server != null) { 145 | getLogger().info("Http Server stoping..."); 146 | server.stop(); 147 | server = null; 148 | WsClientHelper.clear(); 149 | WsClientHelper.updateCoolQ(null); 150 | getLogger().info("Http Server stoped"); 151 | } 152 | } catch (Exception ignored) { 153 | } 154 | } 155 | 156 | private boolean isPortAvailable(int port) { 157 | try { 158 | ServerSocket server = new ServerSocket(port); 159 | server.close(); 160 | return true; 161 | } catch (IOException ignored) { 162 | 163 | } 164 | return false; 165 | } 166 | 167 | private void autoFreedWeb(){ 168 | String rootFolder = "web/"; 169 | File folder = this.getDataFolder(); 170 | folder.mkdir(); 171 | new File(folder,rootFolder).mkdir(); 172 | 173 | try (ZipFile zipFile = new ZipFile(getFile())) { 174 | Enumeration extends ZipEntry> entries = zipFile.entries(); 175 | while (entries.hasMoreElements()) { 176 | ZipEntry entry = entries.nextElement(); 177 | copyFile(zipFile, entry, rootFolder); 178 | } 179 | } catch (IOException ignored) { 180 | 181 | } 182 | } 183 | 184 | private void copyFile(ZipFile zipFile, ZipEntry entry, String rootFolder){ 185 | String name = entry.getName(); 186 | if(!name.startsWith(rootFolder)){ 187 | return; 188 | } 189 | 190 | File file = new File(getDataFolder(), name); 191 | if(entry.isDirectory()) { 192 | file.mkdirs(); 193 | }else { 194 | if (file.exists()){ 195 | return; 196 | } 197 | if (file.getParentFile().isDirectory()){ 198 | file.getParentFile().mkdir(); 199 | } 200 | FileOutputStream outputStream = null; 201 | InputStream inputStream = null; 202 | try { 203 | byte[] buffer = new byte[1024]; 204 | outputStream = new FileOutputStream(file); 205 | inputStream = zipFile.getInputStream(entry); 206 | int len; 207 | while ((len = inputStream.read(buffer)) >= 0) { 208 | outputStream.write(buffer, 0, len); 209 | } 210 | } catch (IOException ignored) { 211 | 212 | } finally { 213 | try { 214 | outputStream.close(); 215 | } catch (IOException ignored) { 216 | 217 | } 218 | try { 219 | inputStream.close(); 220 | } catch (IOException ignored) { 221 | 222 | } 223 | } 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/announcement/Config.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.announcement; 2 | 3 | import org.lintx.plugins.modules.configure.Configure; 4 | import org.lintx.plugins.modules.configure.YamlConfig; 5 | import org.lintx.plugins.yinwuchat.bungee.YinwuChat; 6 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 7 | 8 | import java.io.File; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | @YamlConfig(path = "tasks.yml") 13 | public class Config { 14 | private static Config instance = new Config(); 15 | public static Config getInstance(){ 16 | return instance; 17 | } 18 | 19 | @YamlConfig 20 | List tasks = new ArrayList<>(); 21 | 22 | public void load(YinwuChat plugin){ 23 | tasks = new ArrayList<>(); 24 | Configure.bungeeLoad(plugin,this); 25 | File file = new File(plugin.getDataFolder(),"tasks.yml"); 26 | if (!file.exists()){ 27 | TaskConfig c = new TaskConfig(); 28 | c.enable = true; 29 | c.interval = 300; 30 | c.list.add(new MessageFormat("&e[帮助]","服务器帮助文档","")); 31 | c.list.add(new MessageFormat("&r 在聊天中输入")); 32 | c.list.add(new MessageFormat("&b[i]","在聊天文本中包含这三个字符即可","")); 33 | c.list.add(new MessageFormat("&r可以展示你手中的物品,输入")); 34 | c.list.add(new MessageFormat("&b[i:x]","&b:&r冒号不区分中英文\n&bx&r为背包格子编号\n物品栏为0-8,然后从背包左上角\n从左至右从上至下为9-35\n装备栏为36-39,副手为40","")); 35 | c.list.add(new MessageFormat("&r可以展示背包中x位置对应的物品,一条消息中可以展示多个物品")); 36 | tasks.add(c); 37 | 38 | TaskConfig b = new TaskConfig(); 39 | b.enable = true; 40 | b.interval = 300; 41 | b.list.add(new MessageFormat("&e[帮助]","服务器帮助文档","")); 42 | b.list.add(new MessageFormat("&r 在聊天中输入&b@&r然后后面跟上")); 43 | b.list.add(new MessageFormat("&b玩家名","不区分服务器\n不区分大小写\n可以只输入玩家名的前n个字符\n玩家名后需要跟中文或空格","")); 44 | b.list.add(new MessageFormat("&r即可@该玩家,如果不想被别人@可以输入&b/yinwuchat noat&r命令来切换自己是否允许被他人@")); 45 | tasks.add(b); 46 | Configure.bungeeSave(plugin,this); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/announcement/Task.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.announcement; 2 | 3 | import org.lintx.plugins.yinwuchat.bungee.MessageManage; 4 | 5 | import java.time.Duration; 6 | import java.time.LocalDateTime; 7 | import java.util.List; 8 | 9 | public class Task implements Runnable { 10 | 11 | @Override 12 | public void run() { 13 | List tasks = Config.getInstance().tasks; 14 | if (tasks.size()<=0){ 15 | return; 16 | } 17 | LocalDateTime now = LocalDateTime.now(); 18 | for (TaskConfig task : tasks){ 19 | if (!task.enable){ 20 | continue; 21 | } 22 | if (task.lastTime==null){ 23 | MessageManage.getInstance().broadcast(task.list,task.server); 24 | task.lastTime = now; 25 | } 26 | else { 27 | Duration duration = Duration.between(task.lastTime,now); 28 | if (duration.toMillis()>=task.interval*1000){ 29 | MessageManage.getInstance().broadcast(task.list,task.server); 30 | task.lastTime = now; 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/announcement/TaskConfig.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.announcement; 2 | 3 | import org.lintx.plugins.modules.configure.YamlConfig; 4 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 5 | 6 | import java.time.LocalDateTime; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | @YamlConfig 11 | public class TaskConfig { 12 | @YamlConfig 13 | boolean enable = false; 14 | @YamlConfig 15 | int interval = 30; 16 | @YamlConfig 17 | public List list = new ArrayList<>(); 18 | @YamlConfig 19 | public String server = "all"; 20 | 21 | LocalDateTime lastTime = null; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/config/Config.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.config; 2 | 3 | import org.lintx.plugins.modules.configure.Configure; 4 | import org.lintx.plugins.modules.configure.YamlConfig; 5 | import org.lintx.plugins.yinwuchat.bungee.YinwuChat; 6 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | @YamlConfig 15 | public class Config { 16 | private static int version = 6; 17 | private static Config instance = new Config(); 18 | public static Config getInstance(){ 19 | return instance; 20 | } 21 | 22 | public void load(YinwuChat plugin){ 23 | Configure.bungeeLoad(plugin,this); 24 | if (formatConfig.format==null || formatConfig.format.isEmpty()){ 25 | formatConfig.format = new ArrayList<>(); 26 | formatConfig.format.add(new MessageFormat("&b[Web]","点击打开YinwuChat网页","https://chat.yinwurealm.org")); 27 | formatConfig.format.add(new MessageFormat("&e{displayName}","点击私聊","/msg {displayName}")); 28 | formatConfig.format.add(new MessageFormat(" &6>>> ")); 29 | formatConfig.format.add(new MessageFormat("&r{message}")); 30 | } 31 | if (formatConfig.toFormat==null || formatConfig.toFormat.isEmpty()){ 32 | formatConfig.toFormat = new ArrayList<>(); 33 | formatConfig.toFormat.add(new MessageFormat("&7我 &6-> ")); 34 | formatConfig.toFormat.add(new MessageFormat("&e{displayName}","点击私聊","/msg {displayName}")); 35 | formatConfig.toFormat.add(new MessageFormat(" &6>>> ")); 36 | formatConfig.toFormat.add(new MessageFormat("&r{message}")); 37 | } 38 | if (formatConfig.monitorFormat==null || formatConfig.monitorFormat.isEmpty()){ 39 | formatConfig.monitorFormat = new ArrayList<>(); 40 | formatConfig.monitorFormat.add(new MessageFormat("&7{formPlayer} &6-> ")); 41 | formatConfig.monitorFormat.add(new MessageFormat("&e{toPlayer}")); 42 | formatConfig.monitorFormat.add(new MessageFormat(" &6>>> ")); 43 | formatConfig.monitorFormat.add(new MessageFormat("&r{message}")); 44 | } 45 | if (formatConfig.fromFormat==null || formatConfig.fromFormat.isEmpty()){ 46 | formatConfig.fromFormat = new ArrayList<>(); 47 | formatConfig.fromFormat.add(new MessageFormat("&b[Web]","点击打开YinwuChat网页","https://xxxxxx.xxxx.xxx")); 48 | formatConfig.fromFormat.add(new MessageFormat("&e{displayName}","点击私聊","/msg {displayName}")); 49 | formatConfig.fromFormat.add(new MessageFormat(" &6-> &7我")); 50 | formatConfig.fromFormat.add(new MessageFormat(" &6>>> ")); 51 | formatConfig.fromFormat.add(new MessageFormat("&r{message}")); 52 | } 53 | if (formatConfig.qqFormat==null || formatConfig.qqFormat.isEmpty()){ 54 | formatConfig.qqFormat = new ArrayList<>(); 55 | formatConfig.qqFormat.add(new MessageFormat("&b[QQ群]","点击加入QQ群xxxxx","https://xxxxxx.xxxx.xxx")); 56 | formatConfig.qqFormat.add(new MessageFormat("&e{displayName}")); 57 | formatConfig.qqFormat.add(new MessageFormat(" &6>>> ")); 58 | formatConfig.qqFormat.add(new MessageFormat("&r{message}")); 59 | } 60 | if (configVersion<6){ 61 | redisConfig.selfPrefixFormat = new ArrayList<>(); 62 | redisConfig.selfPrefixFormat.add(new MessageFormat("&8[其他群组]&r","来自其他群组的消息","")); 63 | } 64 | File file = new File(plugin.getDataFolder(),"config.yml"); 65 | if (!file.exists() || version!=configVersion){ 66 | if (file.exists()){ 67 | File bakConfig = new File(plugin.getDataFolder(),"config_v" + configVersion + ".yml"); 68 | try { 69 | Files.copy(file.toPath(),bakConfig.toPath()); 70 | } catch (IOException ignored) { 71 | 72 | } 73 | } 74 | configVersion = version; 75 | save(plugin); 76 | } 77 | } 78 | 79 | public void save(YinwuChat plugin){ 80 | Configure.bungeeSave(plugin,this); 81 | } 82 | 83 | @YamlConfig 84 | public boolean openwsserver = false; 85 | 86 | @YamlConfig 87 | public int wsport = 8888; 88 | 89 | @YamlConfig 90 | public int wsCooldown = 1000; 91 | 92 | @YamlConfig 93 | public String webBATserver = "lobby"; 94 | 95 | @YamlConfig 96 | public int atcooldown = 10; 97 | 98 | @YamlConfig 99 | public String atAllKey = "all"; 100 | 101 | @YamlConfig 102 | public String linkRegex = "((https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|])"; 103 | 104 | @YamlConfig 105 | public List shieldeds = new ArrayList<>(); 106 | 107 | @YamlConfig 108 | public int shieldedMode = 1; 109 | 110 | @YamlConfig 111 | public int shieldedKickTime = 60; 112 | 113 | @YamlConfig 114 | public int shieldedKickCount = 3; 115 | 116 | @YamlConfig 117 | private int configVersion = 0; 118 | 119 | @YamlConfig 120 | public String webDenyStyle = "klmnorxKLMNORX"; 121 | 122 | @YamlConfig 123 | public boolean allowPlayerFormatPrefixSuffix = true; 124 | 125 | @YamlConfig 126 | public String playerFormatPrefixSuffixDenyStyle = "klmnorxKLMNORX"; 127 | 128 | @YamlConfig 129 | public TipsConfig tipsConfig = new TipsConfig(); 130 | 131 | @YamlConfig 132 | public FormatConfig formatConfig = new FormatConfig(); 133 | 134 | @YamlConfig 135 | public CoolQConfig coolQConfig = new CoolQConfig(); 136 | 137 | @YamlConfig 138 | public RedisConfig redisConfig = new RedisConfig(); 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/config/CoolQConfig.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.config; 2 | 3 | import org.lintx.plugins.modules.configure.YamlConfig; 4 | 5 | public class CoolQConfig { 6 | 7 | @YamlConfig 8 | public boolean coolQQQToGame = true; 9 | 10 | @YamlConfig 11 | public String coolqToGameStart = ""; 12 | 13 | @YamlConfig 14 | public boolean coolQGameToQQ = true; 15 | 16 | @YamlConfig 17 | public String gameToCoolqStart = ""; 18 | 19 | @YamlConfig 20 | public String qqDenyStyle = "0-9a-fA-Fk-oK-OrRxX"; 21 | 22 | @YamlConfig 23 | public int coolQGroup = 0; 24 | 25 | @YamlConfig 26 | public String coolQAccessToken = ""; 27 | 28 | @YamlConfig 29 | public String qqAtText = "&7[@{qq}]&r"; 30 | 31 | @YamlConfig 32 | public String qqImageText = "&7[图片]&r"; 33 | 34 | @YamlConfig 35 | public String qqRecordText = "&7[语音]&r"; 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/config/FormatConfig.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.config; 2 | 3 | import org.lintx.plugins.modules.configure.YamlConfig; 4 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 5 | 6 | import java.util.List; 7 | 8 | public class FormatConfig { 9 | 10 | @YamlConfig 11 | public List format = null; 12 | 13 | @YamlConfig 14 | public List qqFormat = null; 15 | 16 | @YamlConfig 17 | public List toFormat = null; 18 | 19 | @YamlConfig 20 | public List fromFormat = null; 21 | 22 | @YamlConfig 23 | public List monitorFormat = null; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/config/PlayerConfig.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.config; 2 | 3 | import net.md_5.bungee.api.connection.ProxiedPlayer; 4 | import org.lintx.plugins.modules.configure.Configure; 5 | import org.lintx.plugins.modules.configure.YamlConfig; 6 | import org.lintx.plugins.yinwuchat.bungee.YinwuChat; 7 | 8 | import java.time.Duration; 9 | import java.time.LocalDateTime; 10 | import java.util.*; 11 | 12 | public class PlayerConfig { 13 | private static PlayerConfig instance = new PlayerConfig(); 14 | private static Map configs = new HashMap<>(); 15 | private static Tokens tokens = null; 16 | public static PlayerConfig getInstance(){ 17 | return instance; 18 | } 19 | 20 | public static Player getConfig(ProxiedPlayer player){ 21 | return getInstance().getPlayerConfig(player); 22 | } 23 | 24 | public static Player getConfig(UUID uuid){ 25 | return getInstance().getPlayerConfig(uuid); 26 | } 27 | 28 | public static void unloadConfig(ProxiedPlayer player){ 29 | UUID uuid = player.getUniqueId(); 30 | if (configs.containsKey(uuid)){ 31 | Player config = configs.get(uuid); 32 | config.save(); 33 | configs.remove(uuid); 34 | } 35 | } 36 | 37 | private Player getPlayerConfig(UUID uuid){ 38 | Player config = configs.get(uuid); 39 | if (config==null){ 40 | String path = "player/" + uuid.toString() + ".yml"; 41 | config = new Player(path); 42 | config.load(); 43 | configs.put(uuid,config); 44 | } 45 | return config; 46 | } 47 | 48 | private Player getPlayerConfig(ProxiedPlayer player){ 49 | UUID uuid = player.getUniqueId(); 50 | Player config = configs.get(uuid); 51 | if (config==null){ 52 | String path = "player/" + uuid.toString() + ".yml"; 53 | config = new Player(path); 54 | config.load(); 55 | configs.put(uuid,config); 56 | } 57 | if (!config.name.equals(player.getName())){ 58 | config.name = player.getName(); 59 | config.save(); 60 | } 61 | return config; 62 | } 63 | 64 | public static Tokens getTokens(){ 65 | return getInstance().getPlayerTokens(); 66 | } 67 | 68 | private Tokens getPlayerTokens(){ 69 | if (tokens==null){ 70 | tokens = new Tokens(); 71 | tokens.load(); 72 | tokens.clearEmptyToken(); 73 | tokens.save(); 74 | } 75 | return tokens; 76 | } 77 | 78 | @YamlConfig(path = "player/nouser.yml") 79 | public static class Player{ 80 | final String path; 81 | 82 | Player(String path){ 83 | this.path = path; 84 | } 85 | 86 | void load(){ 87 | if (this.path.equals("player/nouser.yml")){ 88 | return; 89 | } 90 | Configure.bungeeLoad(YinwuChat.getPlugin(),this,path); 91 | } 92 | 93 | public void save(){ 94 | Configure.bungeeSave(YinwuChat.getPlugin(),this,path); 95 | } 96 | 97 | public boolean isCooldown(){ 98 | if (lastTime.isEqual(LocalDateTime.MIN)){ 99 | return false; 100 | } 101 | Duration duration = Duration.between(lastTime,LocalDateTime.now()); 102 | return duration.toMillis() < Config.getInstance().atcooldown * 1000; 103 | } 104 | 105 | public void updateCooldown(){ 106 | lastTime = LocalDateTime.now(); 107 | } 108 | 109 | private LocalDateTime lastTime = LocalDateTime.MIN; 110 | 111 | public boolean isIgnore(ProxiedPlayer player){ 112 | return ignorePlayer.contains(player.getUniqueId()); 113 | } 114 | 115 | public boolean isIgnore(UUID uuid){ 116 | return ignorePlayer.contains(uuid); 117 | } 118 | 119 | public boolean ignore(UUID uuid){ 120 | if (isIgnore(uuid)){ 121 | ignorePlayer.remove(uuid); 122 | return false; 123 | } 124 | else { 125 | ignorePlayer.add(uuid); 126 | return true; 127 | } 128 | } 129 | 130 | @YamlConfig 131 | public boolean vanish = false; 132 | 133 | @YamlConfig 134 | public boolean monitor = true; 135 | 136 | @YamlConfig 137 | public boolean muteAt = false; 138 | 139 | @YamlConfig 140 | public boolean banAt = false; 141 | 142 | @YamlConfig 143 | List ignorePlayer = new ArrayList<>(); 144 | 145 | @YamlConfig 146 | public String name = ""; 147 | 148 | @YamlConfig 149 | public String publicPrefix = ""; 150 | 151 | @YamlConfig 152 | public String publicSuffix = ""; 153 | 154 | @YamlConfig 155 | public String privatePrefix = ""; 156 | 157 | @YamlConfig 158 | public String privateSuffix = ""; 159 | } 160 | 161 | @YamlConfig(path = "tokens.yml") 162 | public static class Tokens{ 163 | @YamlConfig 164 | Map tokens = new HashMap<>(); 165 | 166 | void clearEmptyToken(){ 167 | tokens.entrySet().removeIf(entry -> entry.getValue() == null || entry.getValue().equals("")); 168 | save(); 169 | } 170 | 171 | public String newToken(){ 172 | UUID uuid = UUID.randomUUID(); 173 | String token = uuid.toString(); 174 | if (tokens.containsKey(token)){ 175 | return newToken(); 176 | } 177 | tokens.put(token,""); 178 | return token; 179 | } 180 | 181 | public boolean bindToken(String token, ProxiedPlayer player){ 182 | if (tokenNotVaild(token)){ 183 | return false; 184 | } 185 | if (tokenIsBind(token)){ 186 | return false; 187 | } 188 | tokens.put(token,player.getUniqueId().toString()); 189 | save(); 190 | return true; 191 | } 192 | 193 | public boolean tokenNotVaild(String token){ 194 | return !tokens.containsKey(token); 195 | } 196 | 197 | public boolean tokenIsBind(String token){ 198 | return tokens.get(token) != null && !tokens.get(token).equals(""); 199 | } 200 | 201 | public UUID getUuid(String token){ 202 | return UUID.fromString(tokens.get(token)); 203 | } 204 | 205 | public void unbindToken(String token){ 206 | tokens.remove(token); 207 | save(); 208 | } 209 | 210 | public List tokenWithPlayer(ProxiedPlayer player){ 211 | List list = new ArrayList<>(); 212 | String pid = player.getUniqueId().toString(); 213 | for (Map.Entry entry:tokens.entrySet()){ 214 | if (entry.getValue().equals(pid)){ 215 | list.add(entry.getKey()); 216 | } 217 | } 218 | return list; 219 | } 220 | 221 | public void save(){ 222 | Configure.bungeeSave(YinwuChat.getPlugin(),this); 223 | } 224 | 225 | void load(){ 226 | Configure.bungeeLoad(YinwuChat.getPlugin(),this); 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.config; 2 | 3 | import org.lintx.plugins.modules.configure.YamlConfig; 4 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class RedisConfig { 10 | @YamlConfig 11 | public boolean openRedis = false; 12 | 13 | @YamlConfig 14 | public String ip = ""; 15 | 16 | @YamlConfig 17 | public int port = 0; 18 | 19 | @YamlConfig 20 | public int maxConnection = 8; 21 | 22 | @YamlConfig 23 | public String password = ""; 24 | 25 | @YamlConfig 26 | public String selfName = "bc1"; 27 | 28 | @YamlConfig 29 | public boolean forwardBcTask = true; 30 | 31 | @YamlConfig 32 | public boolean forwardBcMessageToQQ = true; 33 | 34 | @YamlConfig 35 | public boolean forwardBcMessageToWeb = true; 36 | 37 | @YamlConfig 38 | public boolean forwardBcAtAll = true; 39 | 40 | @YamlConfig 41 | public boolean forwardBcAtOne = true; 42 | 43 | @YamlConfig 44 | public List selfPrefixFormat = new ArrayList<>(); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/config/TipsConfig.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.config; 2 | 3 | import org.lintx.plugins.modules.configure.YamlConfig; 4 | 5 | public class TipsConfig { 6 | 7 | @YamlConfig 8 | public String shieldedKickTip = "你因为发送屏蔽词语,被踢出服务器"; 9 | 10 | @YamlConfig 11 | public String shieldedReplace = "富强、民主、文明、和谐、自由、平等、公正、法治、爱国、敬业、诚信、友善"; 12 | 13 | @YamlConfig 14 | public String atyouselfTip = "&c你不能@你自己"; 15 | 16 | @YamlConfig 17 | public String atyouTip = "&e{player}&b@了你"; 18 | 19 | @YamlConfig 20 | public String cooldownTip = "&c每次使用@功能之间需要等待10秒"; 21 | 22 | @YamlConfig 23 | public String ignoreTip = "&c对方忽略了你,并向你仍了一个烤土豆"; 24 | 25 | @YamlConfig 26 | public String banatTip = "&c对方不想被@,只想安安静静的做一个美男子"; 27 | 28 | @YamlConfig 29 | public String toPlayerNoOnlineTip = "&c对方不在线,无法发送私聊"; 30 | 31 | @YamlConfig 32 | public String msgyouselfTip = "&c你不能私聊你自己"; 33 | 34 | @YamlConfig 35 | public String youismuteTip = "&c你正在禁言中,不能说话"; 36 | 37 | @YamlConfig 38 | public String youisbanTip = "&c你被ban了,不能说话"; 39 | 40 | @YamlConfig 41 | public String shieldedTip = "&c发送的信息中有被屏蔽的词语,无法发送,继续发送将被踢出服务器"; 42 | 43 | @YamlConfig 44 | public String linkText = "&7[&f&l链接&r&7]&r"; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/httpserver/NettyChannelMessageHelper.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.httpserver; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 5 | 6 | public class NettyChannelMessageHelper { 7 | public static void send(Channel channel,String message){ 8 | channel.writeAndFlush(new TextWebSocketFrame(message)); 9 | } 10 | 11 | public static void broadcast(String message){ 12 | for (Channel channel:WsClientHelper.channels()){ 13 | send(channel,message); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/httpserver/NettyHttpRequestHandler.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.httpserver; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.netty.channel.*; 5 | import io.netty.handler.codec.http.*; 6 | import io.netty.handler.codec.http.HttpResponseStatus; 7 | import io.netty.util.AsciiString; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.io.RandomAccessFile; 12 | import java.net.URI; 13 | import java.util.Locale; 14 | 15 | import static io.netty.handler.codec.http.HttpResponseStatus.*; 16 | 17 | public class NettyHttpRequestHandler extends SimpleChannelInboundHandler { 18 | private AsciiString htmlType = AsciiString.cached("text/html"); 19 | private AsciiString jsType = AsciiString.cached("text/javascript"); 20 | private AsciiString cssType = AsciiString.cached("text/css"); 21 | private AsciiString jpegType = AsciiString.cached("image/jpeg"); 22 | private final File rootFolder; 23 | 24 | 25 | NettyHttpRequestHandler(File rootFolder){ 26 | this.rootFolder = rootFolder; 27 | } 28 | 29 | @Override 30 | protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { 31 | try { 32 | URI uri = new URI(request.uri()); 33 | String path = uri.getPath(); 34 | if (path.equals("/")){ 35 | writeIndex(ctx); 36 | return; 37 | } 38 | if (request.method()!=HttpMethod.GET){ 39 | write404(ctx); 40 | return; 41 | } 42 | writeFile(ctx,path); 43 | } 44 | catch (Exception e) { 45 | e.printStackTrace(); 46 | write404(ctx); 47 | } 48 | } 49 | 50 | @Override 51 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 52 | cause.printStackTrace(); 53 | ctx.close(); 54 | } 55 | 56 | private void writeIndex(ChannelHandlerContext ctx){ 57 | writeFile(ctx,"/index.html"); 58 | } 59 | 60 | private void writeFile(ChannelHandlerContext ctx,String path){ 61 | try { 62 | File file = new File(rootFolder,path); 63 | String canonical = file.getCanonicalPath(); 64 | if (canonical.startsWith(rootFolder.getCanonicalPath()) && file.exists() && file.isFile()){ 65 | AsciiString mime; 66 | String cs = canonical.toLowerCase(Locale.ROOT); 67 | if (cs.endsWith(".jpg") || cs.endsWith(".jpeg")){ 68 | mime = jpegType; 69 | }else if (cs.endsWith(".html")){ 70 | mime = htmlType; 71 | }else if (cs.endsWith(".js")){ 72 | mime = jsType; 73 | }else if (cs.endsWith(".css")){ 74 | mime = cssType; 75 | }else { 76 | write404(ctx); 77 | return; 78 | } 79 | 80 | final RandomAccessFile raf = new RandomAccessFile(file,"r"); 81 | long fileLength = raf.length(); 82 | HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK); 83 | HttpHeaders heads = response.headers(); 84 | heads.add(HttpHeaderNames.CONTENT_TYPE, mime + "; charset=UTF-8"); 85 | heads.add(HttpHeaderNames.CONTENT_LENGTH, fileLength); 86 | heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); 87 | ctx.write(response); 88 | 89 | ChannelFuture sendFileFuture = ctx.write(new DefaultFileRegion(raf.getChannel(),0,fileLength),ctx.newProgressivePromise()); 90 | 91 | ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); 92 | sendFileFuture.addListener(new ChannelProgressiveFutureListener() { 93 | @Override 94 | public void operationProgressed(ChannelProgressiveFuture channelProgressiveFuture, long l, long l1) { 95 | 96 | } 97 | 98 | @Override 99 | public void operationComplete(ChannelProgressiveFuture channelProgressiveFuture) throws Exception { 100 | raf.close(); 101 | } 102 | }); 103 | }else { 104 | write404(ctx); 105 | } 106 | } catch (IOException ignored) { 107 | write404(ctx); 108 | } 109 | } 110 | 111 | private void write404(ChannelHandlerContext ctx){ 112 | DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, NOT_FOUND); 113 | String string = "File Not Found"; 114 | response.content().writeBytes(Unpooled.wrappedBuffer(string.getBytes())); 115 | write(ctx,response,htmlType); 116 | } 117 | 118 | private void write(ChannelHandlerContext ctx,DefaultFullHttpResponse response,AsciiString type){ 119 | HttpHeaders heads = response.headers(); 120 | heads.add(HttpHeaderNames.CONTENT_TYPE, type + "; charset=UTF-8"); 121 | heads.add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); 122 | heads.add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); 123 | ctx.writeAndFlush(response); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/httpserver/NettyHttpServer.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.httpserver; 2 | 3 | import io.netty.bootstrap.ServerBootstrap; 4 | import io.netty.channel.Channel; 5 | import io.netty.channel.ChannelInitializer; 6 | import io.netty.channel.nio.NioEventLoopGroup; 7 | import io.netty.channel.socket.SocketChannel; 8 | import io.netty.channel.socket.nio.NioServerSocketChannel; 9 | import io.netty.handler.codec.http.HttpObjectAggregator; 10 | import io.netty.handler.codec.http.HttpServerCodec; 11 | import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 12 | import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; 13 | import org.lintx.plugins.yinwuchat.bungee.YinwuChat; 14 | 15 | import java.io.File; 16 | 17 | public class NettyHttpServer { 18 | private final int port; 19 | private NioEventLoopGroup group; 20 | private final YinwuChat plugin; 21 | private final File rootFolder; 22 | 23 | public NettyHttpServer(int port, YinwuChat plugin, File rootFolder) { 24 | this.port = port; 25 | this.plugin = plugin; 26 | this.rootFolder = rootFolder; 27 | } 28 | 29 | public void start(){ 30 | ServerBootstrap bootstrap = new ServerBootstrap(); 31 | this.group = new NioEventLoopGroup(); 32 | bootstrap.group(group) 33 | .channel(NioServerSocketChannel.class) 34 | .childHandler(new ChannelInitializer() { 35 | @Override 36 | protected void initChannel(SocketChannel channel) { 37 | channel.pipeline() 38 | .addLast(new HttpServerCodec()) 39 | .addLast(new HttpObjectAggregator(65536)) 40 | .addLast(new WebSocketServerCompressionHandler()) 41 | .addLast(new WebSocketServerProtocolHandler("/ws",null,true)) 42 | .addLast(new NettyWebSocketFrameHandler(plugin)) 43 | .addLast(new NettyHttpRequestHandler(rootFolder)); 44 | } 45 | }); 46 | try { 47 | Channel ch = bootstrap.bind(port).sync().channel(); 48 | plugin.getLogger().info("Http Server listener on port:" + port); 49 | ch.closeFuture().sync(); 50 | } catch (InterruptedException ignored) { 51 | }finally { 52 | group.shutdownGracefully(); 53 | } 54 | } 55 | 56 | public void stop(){ 57 | try { 58 | group.shutdownGracefully(); 59 | }catch (Exception | Error ignored){} 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/httpserver/NettyWebSocketFrameHandler.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.httpserver; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.channel.SimpleChannelInboundHandler; 6 | import io.netty.handler.codec.http.HttpHeaders; 7 | import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; 8 | import io.netty.handler.codec.http.websocketx.WebSocketFrame; 9 | import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; 10 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 11 | import org.lintx.plugins.yinwuchat.bungee.MessageManage; 12 | import org.lintx.plugins.yinwuchat.bungee.YinwuChat; 13 | import org.lintx.plugins.yinwuchat.bungee.json.*; 14 | 15 | import java.time.Duration; 16 | import java.time.LocalDateTime; 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | public class NettyWebSocketFrameHandler extends SimpleChannelInboundHandler { 22 | private final YinwuChat plugin; 23 | 24 | NettyWebSocketFrameHandler(YinwuChat plugin){ 25 | this.plugin = plugin; 26 | } 27 | 28 | @Override 29 | protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) { 30 | if (frame instanceof TextWebSocketFrame) { 31 | String request = ((TextWebSocketFrame) frame).text(); 32 | 33 | Channel channel = ctx.channel(); 34 | plugin.getProxy().getScheduler().runAsync(plugin, () -> { 35 | InputBase object = InputBase.getObject(request); 36 | if (object instanceof InputCheckToken) { 37 | InputCheckToken o = (InputCheckToken)object; 38 | 39 | WsClientUtil clientUtil = new WsClientUtil(o.getToken()); 40 | WsClientHelper.add(channel, clientUtil); 41 | 42 | NettyChannelMessageHelper.send(channel,o.getJSON()); 43 | if (!o.getIsvaild()) { 44 | NettyChannelMessageHelper.send(channel,o.getTokenJSON()); 45 | } 46 | else{ 47 | if (o.getIsbind()) { 48 | clientUtil.setUUID(o.getUuid()); 49 | WsClientHelper.kickOtherWS(channel, o.getUuid()); 50 | 51 | OutputPlayerList.sendGamePlayerList(channel); 52 | OutputPlayerList.sendWebPlayerList(); 53 | // String player_name = PlayerConfig.getConfig(o.getUuid()).name; 54 | // YinwuChat.getWSServer().broadcast((new PlayerStatusJSON(player_name,PlayerStatusJSON.PlayerStatus.WEB_JOIN)).getWebStatusJSON()); 55 | // plugin.getProxy().broadcast(ChatUtil.formatJoinMessage(o.getUuid())); 56 | } 57 | } 58 | } 59 | else if (object instanceof InputMessage) { 60 | InputMessage o = (InputMessage)object; 61 | if (o.getMessage().equalsIgnoreCase("")) { 62 | return; 63 | } 64 | 65 | WsClientUtil util = WsClientHelper.get(channel); 66 | if (util != null && util.getUuid() != null) { 67 | if (!util.getLastDate().isEqual(LocalDateTime.MIN)){ 68 | Duration duration = Duration.between(util.getLastDate(), LocalDateTime.now()); 69 | if (duration.toMillis() < Config.getInstance().wsCooldown) { 70 | NettyChannelMessageHelper.send(channel, OutputServerMessage.errorJSON("发送消息过快(当前设置为每条消息之间最少间隔"+(Config.getInstance().wsCooldown)+"毫秒)").getJSON()); 71 | return; 72 | } 73 | } 74 | util.updateLastDate(); 75 | 76 | if (o.getMessage().startsWith("/")) { 77 | String[] command = o.getMessage().replaceFirst("^/", "").split("\\s"); 78 | if (command.length>=3) { 79 | if (command[0].equalsIgnoreCase("msg")) { 80 | String to_player_name = command[1]; 81 | List tmpList = new ArrayList<>(Arrays.asList(command).subList(2, command.length)); 82 | String msg = String.join(" ", tmpList); 83 | 84 | MessageManage.getInstance().handleWebPrivateMessage(channel,util,to_player_name,msg); 85 | } 86 | else{ 87 | NettyChannelMessageHelper.send(channel, OutputServerMessage.errorJSON("发送私聊消息的正确格式为/msg 玩家名 消息").getJSON()); 88 | NettyChannelMessageHelper.send(channel, OutputServerMessage.errorJSON("其他命令暂不支持").getJSON()); 89 | } 90 | } 91 | else{ 92 | NettyChannelMessageHelper.send(channel, OutputServerMessage.errorJSON("发送私聊消息的正确格式为/msg 玩家名 消息").getJSON()); 93 | NettyChannelMessageHelper.send(channel, OutputServerMessage.errorJSON("其他命令暂不支持").getJSON()); 94 | } 95 | return; 96 | } 97 | MessageManage.getInstance().handleWebPublicMessage(util.getUuid(),o.getMessage(),channel); 98 | } 99 | } 100 | else if (object instanceof InputCoolQ){ 101 | InputCoolQ coolMessage = (InputCoolQ)object; 102 | if (coolMessage.getPost_type().equalsIgnoreCase("message") 103 | && coolMessage.getMessage_type().equalsIgnoreCase("group") 104 | && coolMessage.getSub_type().equalsIgnoreCase("normal") 105 | && coolMessage.getGroup_id() == Config.getInstance().coolQConfig.coolQGroup){ 106 | if (!"".equals(Config.getInstance().coolQConfig.coolqToGameStart)){ 107 | if (!coolMessage.getMessage().startsWith(Config.getInstance().coolQConfig.coolqToGameStart)){ 108 | return; 109 | } 110 | } 111 | MessageManage.getInstance().handleQQMessage(coolMessage); 112 | } 113 | } 114 | }); 115 | } else { 116 | String message = "unsupported frame type: " + frame.getClass().getName(); 117 | throw new UnsupportedOperationException(message); 118 | } 119 | } 120 | 121 | @Override 122 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 123 | super.channelInactive(ctx); 124 | //断开连接 125 | if (WsClientHelper.getCoolQ() == ctx.channel()){ 126 | WsClientHelper.updateCoolQ(null); 127 | } 128 | plugin.getProxy().getScheduler().runAsync(plugin, () -> { 129 | WsClientUtil util = WsClientHelper.get(ctx.channel()); 130 | if (util != null) { 131 | WsClientHelper.remove(ctx.channel()); 132 | OutputPlayerList.sendWebPlayerList(); 133 | } 134 | }); 135 | } 136 | 137 | @Override 138 | public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { 139 | if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete){ 140 | WebSocketServerProtocolHandler.HandshakeComplete complete = (WebSocketServerProtocolHandler.HandshakeComplete)evt; 141 | HttpHeaders headers = complete.requestHeaders(); 142 | 143 | String qq = headers.get("X-Self-ID"); 144 | String role = headers.get("X-Client-Role"); 145 | String authorization = headers.get("Authorization"); 146 | if (!"".equals(qq) && "Universal".equals(role)){ 147 | String token = Config.getInstance().coolQConfig.coolQAccessToken; 148 | if (!token.equals("")){ 149 | token = "Token " + token; 150 | if (!token.equals(authorization)){ 151 | ctx.close(); 152 | return; 153 | } 154 | } 155 | WsClientHelper.updateCoolQ(ctx.channel()); 156 | } 157 | else { 158 | OutputPlayerList.sendGamePlayerList(ctx.channel()); 159 | OutputPlayerList.sendWebPlayerList(ctx.channel()); 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/httpserver/WsClientHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.lintx.plugins.yinwuchat.bungee.httpserver; 7 | 8 | import io.netty.channel.Channel; 9 | import org.lintx.plugins.yinwuchat.bungee.json.OutputServerMessage; 10 | 11 | import java.util.*; 12 | 13 | /** 14 | * 15 | * @author LinTx 16 | */ 17 | public class WsClientHelper { 18 | private static final HashMap clients = new HashMap<>(); 19 | private static Channel coolQ = null; 20 | 21 | public static void updateCoolQ(Channel socket){ 22 | if (coolQ!=null){ 23 | try { 24 | coolQ.close(); 25 | }catch (Exception ignored){ 26 | 27 | } 28 | } 29 | coolQ = socket; 30 | } 31 | 32 | public static Channel getCoolQ(){ 33 | return coolQ; 34 | } 35 | 36 | public static void add(Channel channel, WsClientUtil client){ 37 | remove(channel); 38 | clients.put(channel, client); 39 | } 40 | 41 | public static Set channels(){ 42 | return clients.keySet(); 43 | } 44 | 45 | public static Collection utils(){ 46 | return clients.values(); 47 | } 48 | 49 | static void remove(Channel channel){ 50 | clients.remove(channel); 51 | } 52 | 53 | public static WsClientUtil get(Channel channel){ 54 | WsClientUtil client = null; 55 | if (clients.containsKey(channel)) { 56 | client = clients.get(channel); 57 | } 58 | return client; 59 | } 60 | 61 | public static void clear(){ 62 | clients.clear(); 63 | } 64 | 65 | public static Channel getWebSocket(String token){ 66 | for (Map.Entry entry : clients.entrySet()) { 67 | Channel key = entry.getKey(); 68 | WsClientUtil value = entry.getValue(); 69 | if (value.getToken().equalsIgnoreCase(token)) { 70 | return key; 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | public static Channel getWebSocketAsUtil(WsClientUtil util){ 77 | for (Map.Entry entry : clients.entrySet()) { 78 | Channel key = entry.getKey(); 79 | WsClientUtil value = entry.getValue(); 80 | if (value.equals(util)) { 81 | return key; 82 | } 83 | } 84 | return null; 85 | } 86 | 87 | private static List getChannels(UUID uuid){ 88 | List list = new ArrayList<>(); 89 | for (Map.Entry entry : clients.entrySet()) { 90 | Channel key = entry.getKey(); 91 | WsClientUtil value = entry.getValue(); 92 | if (value.getUuid()!=null && uuid.toString().equalsIgnoreCase(value.getUuid().toString())) { 93 | list.add(key); 94 | } 95 | } 96 | return list; 97 | } 98 | 99 | public static void kickOtherWS(Channel channel, UUID uuid){ 100 | List oldChannels = WsClientHelper.getChannels(uuid); 101 | if (!oldChannels.isEmpty()) { 102 | for (Channel oldChannel : oldChannels) { 103 | if (oldChannel != null && oldChannel != channel) { 104 | NettyChannelMessageHelper.send(oldChannel, OutputServerMessage.errorJSON("你的帐号已经在其他地方上线,你已经被踢下线").getJSON()); 105 | oldChannel.close(); 106 | WsClientHelper.remove(oldChannel); 107 | } 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/httpserver/WsClientUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.lintx.plugins.yinwuchat.bungee.httpserver; 7 | 8 | import java.time.LocalDateTime; 9 | import java.util.UUID; 10 | 11 | /** 12 | * 13 | * @author LinTx 14 | */ 15 | public class WsClientUtil { 16 | private UUID uuid = null; 17 | private String token; 18 | private LocalDateTime lastDate; 19 | 20 | private WsClientUtil(String token, UUID uuid){ 21 | this.token = token; 22 | this.uuid = uuid; 23 | this.lastDate = LocalDateTime.MIN; 24 | } 25 | 26 | WsClientUtil(String token){ 27 | this(token, null); 28 | } 29 | 30 | public void setUUID(UUID uuid){ 31 | this.uuid = uuid; 32 | } 33 | 34 | public UUID getUuid(){ 35 | return uuid; 36 | } 37 | 38 | String getToken(){ 39 | return token; 40 | } 41 | 42 | LocalDateTime getLastDate(){ 43 | return lastDate; 44 | } 45 | 46 | void updateLastDate(){ 47 | lastDate = LocalDateTime.now(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/InputBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.lintx.plugins.yinwuchat.bungee.json; 7 | 8 | import com.google.gson.JsonElement; 9 | import com.google.gson.JsonObject; 10 | import com.google.gson.JsonParser; 11 | import com.google.gson.reflect.TypeToken; 12 | import org.lintx.plugins.yinwuchat.Util.Gson; 13 | 14 | /** 15 | * 16 | * @author LinTx 17 | */ 18 | public class InputBase { 19 | 20 | public static InputBase getObject(String json){ 21 | try { 22 | JsonParser parser = new JsonParser(); 23 | JsonElement jsonTree = parser.parse(json); 24 | if (jsonTree.isJsonObject()) { 25 | JsonObject object = jsonTree.getAsJsonObject(); 26 | JsonElement actionElement = object.get("action"); 27 | if (actionElement!=null){ 28 | String action = actionElement.getAsString(); 29 | if (action.equalsIgnoreCase("check_token")) { 30 | return new InputCheckToken(object.get("token").getAsString()); 31 | } 32 | else if (action.equalsIgnoreCase("send_message")) { 33 | return new InputMessage(object.get("message").getAsString()); 34 | } 35 | }else { 36 | JsonElement postTypeElement = object.get("post_type"); 37 | if (postTypeElement!=null){ 38 | InputCoolQ inputModel; 39 | try { 40 | inputModel = Gson.gson().fromJson(json,new TypeToken(){}.getType()); 41 | return inputModel; 42 | }catch (Exception ignored){ 43 | } 44 | } 45 | } 46 | } 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | return null; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/InputCheckToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.lintx.plugins.yinwuchat.bungee.json; 7 | 8 | import com.google.gson.Gson; 9 | import com.google.gson.JsonObject; 10 | import org.lintx.plugins.yinwuchat.bungee.config.PlayerConfig; 11 | import java.util.UUID; 12 | 13 | /** 14 | * 15 | * @author LinTx 16 | */ 17 | public class InputCheckToken extends InputBase { 18 | private Boolean isvaild = false; 19 | private Boolean isbind = false; 20 | private String message = ""; 21 | private String token = ""; 22 | private UUID uuid = null; 23 | 24 | public Boolean getIsvaild(){ 25 | return isvaild; 26 | } 27 | 28 | public Boolean getIsbind(){ 29 | return isbind; 30 | } 31 | 32 | public String getMessage(){ 33 | return message; 34 | } 35 | 36 | public String getToken(){ 37 | return token; 38 | } 39 | 40 | public UUID getUuid(){ 41 | return uuid; 42 | } 43 | 44 | public String getJSON(){ 45 | JsonObject json = new JsonObject(); 46 | json.addProperty("action", "check_token"); 47 | json.addProperty("isbind", isbind); 48 | json.addProperty("status", isvaild); 49 | json.addProperty("message", message); 50 | return new Gson().toJson(json); 51 | } 52 | 53 | public String getTokenJSON(){ 54 | JsonObject json = new JsonObject(); 55 | json.addProperty("action", "update_token"); 56 | json.addProperty("token", token); 57 | return new Gson().toJson(json); 58 | } 59 | 60 | InputCheckToken(String token){ 61 | this(token,true); 62 | } 63 | 64 | public InputCheckToken(String token,Boolean autoNewToken){ 65 | this.token = token; 66 | PlayerConfig.Tokens tokens = PlayerConfig.getTokens(); 67 | if (token==null || token.equalsIgnoreCase("")) { 68 | if (autoNewToken) { 69 | message = "生成了新的token"; 70 | this.token = tokens.newToken(); 71 | } 72 | } 73 | else{ 74 | if (tokens.tokenNotVaild(token)) { 75 | message = "token无效"; 76 | if (autoNewToken) { 77 | message += ",生成了新的token"; 78 | this.token = tokens.newToken(); 79 | } 80 | } 81 | else{ 82 | isvaild = true; 83 | message = "success"; 84 | isbind = tokens.tokenIsBind(token); 85 | if (isbind){ 86 | uuid = tokens.getUuid(token); 87 | } 88 | } 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/InputCoolQ.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.json; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class InputCoolQ extends InputBase { 6 | @SerializedName("post_type") 7 | private String post_type=""; 8 | @SerializedName("message_type") 9 | private String message_type=""; 10 | @SerializedName("sub_type") 11 | private String sub_type=""; 12 | @SerializedName("group_id") 13 | private int group_id=0; 14 | @SerializedName("message") 15 | private String message=""; 16 | @SerializedName("raw_message") 17 | private String raw_message=""; 18 | @SerializedName("sender") 19 | private Sender sender=new Sender(); 20 | 21 | public String getPost_type() { 22 | return post_type; 23 | } 24 | 25 | public String getMessage_type() { 26 | return message_type; 27 | } 28 | 29 | public String getSub_type() { 30 | return sub_type; 31 | } 32 | 33 | public int getGroup_id() { 34 | return group_id; 35 | } 36 | 37 | public String getMessage() { 38 | return message; 39 | } 40 | 41 | public String getRaw_message() { 42 | return raw_message; 43 | } 44 | 45 | public Sender getSender() { 46 | return sender; 47 | } 48 | 49 | public static class Sender{ 50 | @SerializedName("nickname") 51 | private String nickname=""; 52 | @SerializedName("card") 53 | private String card=""; 54 | 55 | public String getNickname() { 56 | return nickname; 57 | } 58 | 59 | public String getCard() { 60 | return card; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/InputMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.lintx.plugins.yinwuchat.bungee.json; 7 | 8 | /** 9 | * 10 | * @author LinTx 11 | */ 12 | public class InputMessage extends InputBase { 13 | private final String message; 14 | 15 | public String getMessage(){ 16 | return message; 17 | } 18 | 19 | InputMessage(String message){ 20 | if (message == null) { 21 | message = ""; 22 | } 23 | this.message = message; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/OutputCoolQ.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.json; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import org.lintx.plugins.yinwuchat.Util.Gson; 5 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 6 | 7 | public class OutputCoolQ { 8 | public OutputCoolQ(String message){ 9 | this.params = new Params(message); 10 | } 11 | 12 | @SerializedName("action") 13 | private String action="send_group_msg"; 14 | @SerializedName("params") 15 | private Params params; 16 | 17 | public static class Params{ 18 | Params(String message){ 19 | this.message = message; 20 | } 21 | @SerializedName("group_id") 22 | private int group_id= Config.getInstance().coolQConfig.coolQGroup; 23 | @SerializedName("message") 24 | private String message=""; 25 | @SerializedName("auto_escape") 26 | private boolean auto_escape=true; 27 | } 28 | 29 | public String getJSON(){ 30 | return Gson.gson().toJson(this); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/OutputPlayerList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.lintx.plugins.yinwuchat.bungee.json; 7 | 8 | import com.google.gson.Gson; 9 | import com.google.gson.JsonArray; 10 | import com.google.gson.JsonObject; 11 | import io.netty.channel.Channel; 12 | import net.md_5.bungee.api.config.ServerInfo; 13 | import net.md_5.bungee.api.connection.ProxiedPlayer; 14 | import org.lintx.plugins.yinwuchat.bungee.config.PlayerConfig; 15 | import org.lintx.plugins.yinwuchat.bungee.YinwuChat; 16 | import org.lintx.plugins.yinwuchat.bungee.httpserver.NettyChannelMessageHelper; 17 | import org.lintx.plugins.yinwuchat.bungee.httpserver.NettyHttpServer; 18 | import org.lintx.plugins.yinwuchat.bungee.httpserver.WsClientHelper; 19 | import org.lintx.plugins.yinwuchat.bungee.httpserver.WsClientUtil; 20 | 21 | /** 22 | * 23 | * @author LinTx 24 | */ 25 | public class OutputPlayerList { 26 | private static String getGamePlayerList(){ 27 | JsonArray jsonArray = new JsonArray(); 28 | for (ServerInfo serverInfo:YinwuChat.getPlugin().getProxy().getServers().values()){ 29 | for (ProxiedPlayer player : serverInfo.getPlayers()){ 30 | PlayerConfig.Player playerConfig = PlayerConfig.getConfig(player); 31 | if (playerConfig.vanish){ 32 | continue; 33 | } 34 | JsonObject jsonObject = new JsonObject(); 35 | jsonObject.addProperty("player_name", player.getName()); 36 | String server_name = serverInfo.getName(); 37 | jsonObject.addProperty("server_name", server_name); 38 | jsonArray.add(jsonObject); 39 | } 40 | } 41 | JsonObject resultJsonObject = new JsonObject(); 42 | resultJsonObject.addProperty("action", "game_player_list"); 43 | resultJsonObject.add("player_list", jsonArray); 44 | return new Gson().toJson(resultJsonObject); 45 | } 46 | 47 | public static void sendGamePlayerList(Channel channel){ 48 | NettyHttpServer server = YinwuChat.getWSServer(); 49 | if (server!=null) { 50 | NettyChannelMessageHelper.send(channel,getGamePlayerList()); 51 | } 52 | } 53 | 54 | public static void sendGamePlayerList(){ 55 | NettyHttpServer server = YinwuChat.getWSServer(); 56 | if (server!=null) { 57 | NettyChannelMessageHelper.broadcast(getGamePlayerList()); 58 | } 59 | } 60 | 61 | private static String getWebPlayerList(){ 62 | JsonArray jsonArray = new JsonArray(); 63 | for (WsClientUtil util : WsClientHelper.utils()) { 64 | if (util.getUuid()==null){ 65 | continue; 66 | } 67 | PlayerConfig.Player config = PlayerConfig.getConfig(util.getUuid()); 68 | if (config.name==null || config.name.equals("")){ 69 | continue; 70 | } 71 | jsonArray.add(config.name); 72 | } 73 | JsonObject resultJsonObject = new JsonObject(); 74 | resultJsonObject.addProperty("action", "web_player_list"); 75 | resultJsonObject.add("player_list", jsonArray); 76 | return new Gson().toJson(resultJsonObject); 77 | } 78 | 79 | public static void sendWebPlayerList(Channel channel){ 80 | NettyHttpServer server = YinwuChat.getWSServer(); 81 | if (server!=null) { 82 | NettyChannelMessageHelper.send(channel,getWebPlayerList()); 83 | } 84 | } 85 | 86 | public static void sendWebPlayerList(){ 87 | NettyHttpServer server = YinwuChat.getWSServer(); 88 | if (server!=null) { 89 | NettyChannelMessageHelper.broadcast(getWebPlayerList()); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/OutputServerMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package org.lintx.plugins.yinwuchat.bungee.json; 7 | 8 | import com.google.gson.Gson; 9 | import com.google.gson.JsonObject; 10 | 11 | import java.util.Date; 12 | 13 | /** 14 | * 15 | * @author LinTx 16 | */ 17 | public class OutputServerMessage { 18 | private final String message; 19 | private final String action = "server_message"; 20 | private final int status; 21 | private final static String ERROR_COLOR = "&c"; 22 | private final static String SUCCESS_COLOR = "&a"; 23 | private final static String INFO_COLOR = "&6"; 24 | 25 | private OutputServerMessage(String message, int status){ 26 | this.message = message; 27 | this.status = status; 28 | } 29 | 30 | public String getJSON(){ 31 | JsonObject json = new JsonObject(); 32 | json.addProperty("action", action); 33 | json.addProperty("message", message); 34 | json.addProperty("time", new Date().getTime()); 35 | json.addProperty("status", status); 36 | return new Gson().toJson(json); 37 | } 38 | 39 | public static OutputServerMessage errorJSON(String message){ 40 | return new OutputServerMessage(OutputServerMessage.ERROR_COLOR + message,1); 41 | } 42 | 43 | public static OutputServerMessage errorJSON(String message, int status){ 44 | return new OutputServerMessage(OutputServerMessage.ERROR_COLOR + message,status); 45 | } 46 | 47 | public static OutputServerMessage successJSON(String message){ 48 | return new OutputServerMessage(OutputServerMessage.SUCCESS_COLOR + message,0); 49 | } 50 | 51 | public static OutputServerMessage infoJSON(String message){ 52 | return new OutputServerMessage(OutputServerMessage.INFO_COLOR + message,0); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/RedisMessage.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.json; 2 | 3 | import net.md_5.bungee.api.chat.BaseComponent; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.UUID; 8 | 9 | public class RedisMessage { 10 | public RedisMessageType type = RedisMessageType.UNKNOWN; 11 | public String toPlayerName = ""; 12 | public String message = ""; 13 | public transient BaseComponent chat; 14 | public String fromServer = ""; 15 | public UUID fromPlayerUUID; 16 | public String toServer = ""; 17 | public String toMCServer = ""; 18 | public List playerList = new ArrayList<>(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/bungee/json/RedisMessageType.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.bungee.json; 2 | 3 | public enum RedisMessageType { 4 | UNKNOWN, 5 | PUBLIC_MESSAGE, 6 | PRIVATE_MESSAGE, 7 | AT_PLAYER, 8 | AT_PLAYER_ALL, 9 | PLAYER_LIST, 10 | TASK 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/BungeeAtPlayerHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import com.google.common.io.ByteArrayDataOutput; 4 | import com.google.common.io.ByteStreams; 5 | import net.md_5.bungee.api.chat.TextComponent; 6 | import net.md_5.bungee.api.config.ServerInfo; 7 | import net.md_5.bungee.api.connection.ProxiedPlayer; 8 | import org.lintx.plugins.yinwuchat.Const; 9 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 10 | import org.lintx.plugins.yinwuchat.bungee.RedisUtil; 11 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 12 | import org.lintx.plugins.yinwuchat.bungee.config.PlayerConfig; 13 | import org.lintx.plugins.yinwuchat.bungee.YinwuChat; 14 | import org.lintx.plugins.yinwuchat.bungee.json.RedisMessageType; 15 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 16 | import org.lintx.plugins.yinwuchat.chat.struct.BungeeChatPlayer; 17 | import org.lintx.plugins.yinwuchat.chat.struct.ChatSource; 18 | import org.lintx.plugins.yinwuchat.chat.struct.ChatType; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Locale; 23 | 24 | public class BungeeAtPlayerHandle extends ChatHandle { 25 | private List atPlayers; 26 | private Config config = Config.getInstance(); 27 | private boolean isSendPermissionTip; 28 | private BungeeChatPlayer player; 29 | 30 | @Override 31 | public void handle(Chat chat) { 32 | if (chat.source!= ChatSource.GAME) return; 33 | if (chat.type!= ChatType.PUBLIC) return; 34 | if (!(chat.fromPlayer instanceof BungeeChatPlayer)) return; 35 | player = (BungeeChatPlayer)chat.fromPlayer; 36 | if (player.player==null) return; 37 | 38 | atPlayers = new ArrayList<>(); 39 | isSendPermissionTip = false; 40 | atAll(chat); 41 | atOne(chat); 42 | 43 | if (!player.player.hasPermission(Const.PERMISSION_COOL_DOWN_BYPASS)){ 44 | if (atPlayers.size()>0) chat.fromPlayer.config.updateCooldown(); 45 | } 46 | } 47 | 48 | private void atAll(Chat chat){ 49 | if (!player.player.hasPermission(Const.PERMISSION_AT_ALL)) return; 50 | String regexp = "@(\\w*?)("+config.atAllKey+")(?=\\W|$)"; 51 | 52 | handle(chat, regexp, (matcher) -> { 53 | // MessageFormat extra = new MessageFormat(); 54 | TextComponent component = new TextComponent(); 55 | String server = matcher.group(1).toLowerCase(); 56 | if ("".equals(server)){ 57 | for (ProxiedPlayer p: YinwuChat.getPlugin().getProxy().getPlayers()){ 58 | if (!p.equals(player.player)){ 59 | if (atPlayers.contains(p)) continue; 60 | if (atPlayer(player,p,true)){ 61 | atPlayers.add(p); 62 | } 63 | } 64 | } 65 | if (config.redisConfig.openRedis){ 66 | RedisUtil.sendMessage(RedisMessageType.AT_PLAYER_ALL,player.player.getUniqueId(),new TextComponent(MessageUtil.replace(config.tipsConfig.atyouTip.replaceAll("\\{player}",player.playerName))),""); 67 | } 68 | component.setText("§b" + matcher.group(0) + "§r"); 69 | return component; 70 | } 71 | 72 | ServerInfo atServer = null; 73 | ServerInfo findServer = null; 74 | for (ServerInfo serverInfo : YinwuChat.getPlugin().getProxy().getServers().values()){ 75 | String serverName = serverInfo.getName().toLowerCase(Locale.ROOT); 76 | if (serverName.equalsIgnoreCase(server)){ 77 | atServer = serverInfo; 78 | break; 79 | } 80 | if (serverName.startsWith(server)){ 81 | findServer = serverInfo; 82 | } 83 | } 84 | if (atServer==null){ 85 | atServer = findServer; 86 | } 87 | if (atServer!=null){ 88 | for (ProxiedPlayer p: YinwuChat.getPlugin().getProxy().getServerInfo(atServer.getName()).getPlayers()){ 89 | if (!p.equals(player.player)){ 90 | if (atPlayers.contains(p)) continue; 91 | if (atPlayer(player,p,true)){ 92 | atPlayers.add(p); 93 | } 94 | } 95 | } 96 | if (config.redisConfig.openRedis){ 97 | RedisUtil.sendMessage(RedisMessageType.AT_PLAYER_ALL,player.player.getUniqueId(),new TextComponent(MessageUtil.replace(config.tipsConfig.atyouTip.replaceAll("\\{player}",player.playerName))),""); 98 | } 99 | 100 | component.setText("§b@" +atServer.getName() + matcher.group(2) + "§r"); 101 | return component; 102 | } 103 | return null; 104 | }); 105 | } 106 | 107 | private void atOne(Chat chat){ 108 | String regexp = "@(\\w*?)(?=\\W|$)"; 109 | handle(chat, regexp, (matcher) -> { 110 | System.out.println(matcher.group(0)); 111 | ProxiedPlayer atPlayer = null; 112 | ProxiedPlayer findPlayer = null; 113 | String str = matcher.group(1).toLowerCase(Locale.ROOT); 114 | if (str.equals("")){ 115 | return null; 116 | } 117 | for (ProxiedPlayer p: YinwuChat.getPlugin().getProxy().getPlayers()){ 118 | PlayerConfig.Player pc = PlayerConfig.getConfig(p); 119 | if (!pc.vanish){ 120 | String player_name = p.getName().toLowerCase(Locale.ROOT); 121 | if (player_name.equalsIgnoreCase(str)){ 122 | atPlayer = p; 123 | break; 124 | } 125 | if (player_name.startsWith(str)){ 126 | findPlayer = p; 127 | } 128 | } 129 | } 130 | if (atPlayer==null){ 131 | atPlayer = findPlayer; 132 | } 133 | if (atPlayer!=null){ 134 | if (atPlayer.equals(player.player)){ 135 | atPlayer.sendMessage(new TextComponent(MessageUtil.replace(config.tipsConfig.atyouselfTip))); 136 | return null; 137 | } 138 | if (atPlayers.contains(atPlayer)) return null; 139 | if (atPlayer(player,atPlayer,false)){ 140 | atPlayers.add(atPlayer); 141 | return new TextComponent("§b@" + atPlayer.getName() + "§r"); 142 | } 143 | } 144 | if (config.redisConfig.openRedis){ 145 | String findPlayerName = null; 146 | String toPlayerName = null; 147 | for (String rpn : RedisUtil.playerList.keySet()){ 148 | String pn = rpn.toLowerCase(Locale.ROOT); 149 | if (pn.equals(str)) { 150 | toPlayerName = rpn; 151 | break; 152 | } 153 | if (pn.startsWith(str)) { 154 | findPlayerName = rpn; 155 | } 156 | } 157 | if (toPlayerName == null && findPlayerName!=null) { 158 | toPlayerName = findPlayerName; 159 | } 160 | if (toPlayerName!=null){ 161 | RedisUtil.sendMessage(RedisMessageType.AT_PLAYER,player.player.getUniqueId(),new TextComponent(MessageUtil.replace(config.tipsConfig.atyouTip.replaceAll("\\{player}",player.playerName))),toPlayerName); 162 | return new TextComponent("§b@" + toPlayerName + "§r"); 163 | } 164 | } 165 | return null; 166 | }); 167 | } 168 | 169 | private boolean atPlayer(BungeeChatPlayer player, ProxiedPlayer atPlayer, boolean atAll){ 170 | PlayerConfig.Player pc = PlayerConfig.getConfig(atPlayer); 171 | Config config = Config.getInstance(); 172 | if (!atAll){ 173 | if (pc.isIgnore(player.player)){ 174 | player.player.sendMessage(MessageUtil.newTextComponent(MessageUtil.replace(config.tipsConfig.ignoreTip))); 175 | return false; 176 | } 177 | if (pc.banAt){ 178 | player.player.sendMessage(MessageUtil.newTextComponent(MessageUtil.replace(config.tipsConfig.banatTip))); 179 | return false; 180 | } 181 | } 182 | if (!player.player.hasPermission(Const.PERMISSION_COOL_DOWN_BYPASS)){ 183 | if (player.config.isCooldown()){ 184 | if (!isSendPermissionTip) 185 | player.player.sendMessage(MessageUtil.newTextComponent(MessageUtil.replace(config.tipsConfig.cooldownTip))); 186 | isSendPermissionTip = true; 187 | return false; 188 | } 189 | } 190 | atPlayer.sendMessage(MessageUtil.newTextComponent(MessageUtil.replace(config.tipsConfig.atyouTip.replaceAll("\\{player}",player.playerName)))); 191 | 192 | if (!atAll && pc.muteAt){ 193 | return true; 194 | } 195 | ByteArrayDataOutput output = ByteStreams.newDataOutput(); 196 | output.writeUTF(Const.PLUGIN_SUB_CHANNEL_AT); 197 | atPlayer.getServer().sendData(Const.PLUGIN_CHANNEL,output.toByteArray()); 198 | return true; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/ChatHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import net.md_5.bungee.api.chat.BaseComponent; 4 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 5 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 6 | import org.lintx.plugins.yinwuchat.chat.struct.ChatStruct; 7 | 8 | import java.util.ListIterator; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | public abstract class ChatHandle { 13 | void handle(Chat chat, String regexp, HandleComponentCallback callback) { 14 | ListIterator iterator = chat.chat.listIterator(); 15 | while (iterator.hasNext()){ 16 | ChatStruct struct = iterator.next(); 17 | Pattern pattern = Pattern.compile(regexp); 18 | Matcher matcher = pattern.matcher(struct.chat); 19 | while (matcher.find()){ 20 | String[] splits = struct.chat.split(regexp,2); 21 | if (splits.length!=2) break; 22 | 23 | BaseComponent component = callback.handle(matcher); 24 | if (component==null){ 25 | component = MessageUtil.newTextComponent(matcher.group(0)); 26 | } 27 | 28 | ChatStruct child = new ChatStruct(); 29 | child.chat = splits[0]; 30 | child.component = component; 31 | 32 | iterator.previous(); 33 | iterator.add(child); 34 | iterator.next(); 35 | 36 | struct.chat = splits[1]; 37 | 38 | matcher = pattern.matcher(struct.chat); 39 | } 40 | } 41 | } 42 | 43 | void handle(Chat chat, HandleTextCallback callback){ 44 | for (ChatStruct struct : chat.chat) { 45 | struct.chat = callback.handle(struct.chat); 46 | } 47 | } 48 | 49 | public abstract void handle(Chat chat); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/CoolQCodeHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import net.md_5.bungee.api.chat.TextComponent; 4 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 5 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 6 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 7 | import org.lintx.plugins.yinwuchat.chat.struct.ChatSource; 8 | 9 | public class CoolQCodeHandle extends ChatHandle { 10 | @Override 11 | public void handle(Chat chat) { 12 | if (chat.source!= ChatSource.QQ) return; 13 | Config config = Config.getInstance(); 14 | String regexp = "\\[CQ:(.*?),(.*?)]"; 15 | handle(chat, regexp, (matcher) -> { 16 | String func = matcher.group(1); 17 | String ext = matcher.group(2); 18 | 19 | TextComponent component = new TextComponent(); 20 | if (func.equalsIgnoreCase("image")){ 21 | component.setText(MessageUtil.replace(config.coolQConfig.qqImageText)); 22 | }else if (func.equalsIgnoreCase("record")){ 23 | component.setText(MessageUtil.replace(config.coolQConfig.qqRecordText)); 24 | }else if (func.equalsIgnoreCase("at")){ 25 | component.setText(MessageUtil.replace(config.coolQConfig.qqAtText.replaceAll("\\{qq}",ext.replaceAll("qq=","")))); 26 | }else if (func.equalsIgnoreCase("share")){ 27 | String url = ""; 28 | String[] a = ext.split(","); 29 | for (String kv : a) { 30 | String[] b = kv.split("=",2); 31 | if (b.length==2 && b[0].equalsIgnoreCase("url")){ 32 | url = b[1]; 33 | break; 34 | } 35 | } 36 | component.setText(MessageUtil.replace(config.tipsConfig.linkText)); 37 | if (!"".equals(url)){ 38 | chat.setHover(component,url); 39 | chat.setClick(component,url); 40 | } 41 | }else { 42 | return null; 43 | } 44 | return component; 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/CoolQEscapeHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 4 | 5 | public class CoolQEscapeHandle extends ChatHandle { 6 | @Override 7 | public void handle(Chat chat) { 8 | handle(chat, message -> message.replaceAll("&","& ").replaceAll("[","[").replaceAll("]","]")); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/EmojiHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 4 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 5 | 6 | public class EmojiHandle extends ChatHandle { 7 | @Override 8 | public void handle(Chat chat) { 9 | handle(chat, MessageUtil::removeEmoji); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/ExtraDataHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import net.md_5.bungee.api.chat.TextComponent; 4 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 5 | import org.lintx.plugins.yinwuchat.chat.struct.ChatSource; 6 | import org.lintx.plugins.yinwuchat.json.HandleConfig; 7 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 8 | 9 | 10 | public class ExtraDataHandle extends ChatHandle { 11 | 12 | @Override 13 | public void handle(Chat chat) { 14 | if (chat.source!= ChatSource.GAME) return; 15 | if (chat.extraData==null || chat.extraData.isEmpty()) return; 16 | for (HandleConfig config:chat.extraData){ 17 | handle(chat, config.placeholder, (matcher) -> { 18 | TextComponent component = new TextComponent(); 19 | for (MessageFormat format:config.format){ 20 | TextComponent c = chat.buildFormat(format); 21 | if (c!=null) component.addExtra(c); 22 | } 23 | if (component.getExtra()==null || component.getExtra().isEmpty()) return null; 24 | return component; 25 | }); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/HandleComponentCallback.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import net.md_5.bungee.api.chat.BaseComponent; 4 | 5 | import java.util.regex.Matcher; 6 | 7 | public interface HandleComponentCallback { 8 | BaseComponent handle(Matcher matcher); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/HandleTextCallback.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | public interface HandleTextCallback { 4 | String handle(String message); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/ItemShowHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import org.lintx.plugins.yinwuchat.Const; 4 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 5 | import org.lintx.plugins.yinwuchat.chat.struct.ChatSource; 6 | 7 | public class ItemShowHandle extends ChatHandle { 8 | @Override 9 | public void handle(Chat chat) { 10 | if (chat.source!= ChatSource.GAME) return; 11 | if (chat.items==null) return; 12 | 13 | handle(chat, Const.ITEM_PLACEHOLDER, (matcher) -> { 14 | if (chat.items.isEmpty()) return null; 15 | return chat.items.remove(0); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/LinkHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import net.md_5.bungee.api.chat.TextComponent; 4 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 5 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 6 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 7 | 8 | public class LinkHandle extends ChatHandle { 9 | @Override 10 | public void handle(Chat chat) { 11 | Config config = Config.getInstance(); 12 | String regexp = config.linkRegex; 13 | handle(chat, regexp, (matcher) -> { 14 | String link = matcher.group(0); 15 | TextComponent component = MessageUtil.newTextComponent(MessageUtil.replace(config.tipsConfig.linkText)); 16 | chat.setHover(component,link); 17 | chat.setClick(component,link); 18 | return component; 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/StylePermissionHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 4 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 5 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 6 | import org.lintx.plugins.yinwuchat.chat.struct.ChatSource; 7 | 8 | public class StylePermissionHandle extends ChatHandle { 9 | @Override 10 | public void handle(Chat chat) { 11 | Config config = Config.getInstance(); 12 | if (chat.source== ChatSource.QQ){ 13 | handle(chat, message -> MessageUtil.filter(message,config.coolQConfig.qqDenyStyle)); 14 | }else if (chat.source==ChatSource.WEB){ 15 | handle(chat, message -> MessageUtil.filter(message,config.webDenyStyle)); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/handle/StyleSymbolHandle.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.handle; 2 | 3 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 4 | import org.lintx.plugins.yinwuchat.chat.struct.Chat; 5 | import org.lintx.plugins.yinwuchat.chat.struct.ChatSource; 6 | 7 | public class StyleSymbolHandle extends ChatHandle { 8 | @Override 9 | public void handle(Chat chat) { 10 | if (chat.source!= ChatSource.QQ) handle(chat, MessageUtil::replace); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/struct/BukkitChatPlayer.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.struct; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | public class BukkitChatPlayer extends ChatPlayer { 6 | public Player player; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/struct/BungeeChatPlayer.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.struct; 2 | 3 | import net.md_5.bungee.api.connection.ProxiedPlayer; 4 | 5 | public class BungeeChatPlayer extends ChatPlayer { 6 | public ProxiedPlayer player; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/struct/Chat.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.struct; 2 | 3 | import net.md_5.bungee.api.chat.BaseComponent; 4 | import net.md_5.bungee.api.chat.ClickEvent; 5 | import net.md_5.bungee.api.chat.HoverEvent; 6 | import net.md_5.bungee.api.chat.TextComponent; 7 | import org.lintx.plugins.yinwuchat.Util.MessageUtil; 8 | import org.lintx.plugins.yinwuchat.bungee.config.Config; 9 | import org.lintx.plugins.yinwuchat.json.HandleConfig; 10 | import org.lintx.plugins.yinwuchat.json.MessageFormat; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | public class Chat { 18 | public List chat; 19 | public ChatSource source; 20 | public ChatType type; 21 | public ChatPlayer fromPlayer; 22 | private ChatPlayer toPlayer; 23 | public List extraData = new ArrayList<>(); 24 | public List items; 25 | private Config config = Config.getInstance(); 26 | private TextComponent component; 27 | 28 | public Chat(){ 29 | fromPlayer = new ChatPlayer(); 30 | } 31 | 32 | public Chat(ChatPlayer fromPlayer, List chat, ChatSource source){ 33 | this(fromPlayer,new ChatPlayer(),chat,source,ChatType.PUBLIC); 34 | } 35 | 36 | public Chat(ChatPlayer fromPlayer, ChatPlayer toPlayer, List chat, ChatSource source){ 37 | this(fromPlayer,toPlayer,chat,source,ChatType.PRIVATE); 38 | } 39 | 40 | public Chat(ChatPlayer fromPlayer, ChatPlayer toPlayer, List chat, ChatSource source, ChatType type){ 41 | this.fromPlayer = fromPlayer; 42 | this.toPlayer = toPlayer; 43 | this.chat = chat; 44 | this.source = source; 45 | this.type = type; 46 | } 47 | 48 | //build一个公屏消息 49 | public TextComponent buildPublicMessage(List formats){ 50 | return buildMessage(formats,fromPlayer); 51 | } 52 | 53 | //build一个别人发给我的私聊消息 54 | public TextComponent buildPrivateFormMessage(List formats){ 55 | return buildMessage(formats,fromPlayer); 56 | } 57 | 58 | //build一个我发给别人的私聊消息 59 | public TextComponent buildPrivateToMessage(List formats){ 60 | return buildMessage(formats,toPlayer); 61 | } 62 | 63 | //build一个OP监听的私聊消息 64 | public TextComponent buildPrivateMonitorMessage(List formats){ 65 | return buildMessage(formats); 66 | } 67 | 68 | //build一个只带一个玩家名的消息 69 | private TextComponent buildMessage(List formats, ChatPlayer player){ 70 | TextComponent textComponent = new TextComponent(); 71 | TextComponent message = build(); 72 | for (MessageFormat format:formats){ 73 | if (format.message==null || "".equals(format.message)) continue; 74 | TextComponent text = new TextComponent(); 75 | MessageFormat newFormat = handlePlayerName(format, player.playerName); 76 | 77 | buildMessage(textComponent, message, newFormat, text); 78 | } 79 | return textComponent; 80 | } 81 | 82 | //build一个带2个玩家名的消息 83 | private TextComponent buildMessage(List formats){ 84 | TextComponent textComponent = new TextComponent(); 85 | TextComponent message = build(); 86 | for (MessageFormat format:formats){ 87 | if (format.message==null || "".equals(format.message)) continue; 88 | TextComponent text = new TextComponent(); 89 | MessageFormat newFormat = handlePlayerName(format,fromPlayer.playerName,toPlayer.playerName); 90 | 91 | buildMessage(textComponent, message, newFormat, text); 92 | } 93 | return textComponent; 94 | } 95 | 96 | //build消息核心 97 | private void buildMessage(TextComponent textComponent, TextComponent message, MessageFormat format, TextComponent text) { 98 | setComponentEvent(format, text); 99 | 100 | if (format.message!=null && format.message.contains("{message}")){ 101 | int index = format.message.indexOf("{message}"); 102 | String msg1 = format.message.substring(0,index); 103 | String msg2 = format.message.substring(index+"{message}".length()); 104 | // text.setText(msg1); 105 | text.addExtra(MessageUtil.newTextComponent(msg1)); 106 | text.addExtra(message); 107 | // text.addExtra(msg2); 108 | text.addExtra(MessageUtil.newTextComponent(msg2)); 109 | } 110 | else { 111 | // text.setText(format.message); 112 | text.addExtra(MessageUtil.newTextComponent(format.message)); 113 | } 114 | 115 | textComponent.addExtra(text); 116 | } 117 | 118 | //build消息本体 119 | private TextComponent build(){ 120 | if (this.component!=null) return this.component; 121 | TextComponent component = new TextComponent(); 122 | for (ChatStruct chat:this.chat){ 123 | if (null!=chat.chat && !"".equals(chat.chat)) component.addExtra(MessageUtil.newTextComponent(chat.chat)); 124 | if (null!=chat.component) component.addExtra(chat.component); 125 | } 126 | this.component = component; 127 | return component; 128 | } 129 | 130 | //将format build成textcomponent 131 | public TextComponent buildFormat(MessageFormat format){ 132 | if (format.message==null || "".equals(format.message)) return null; 133 | TextComponent component = new TextComponent(); 134 | MessageFormat newFormat = handlePlayerName(format); 135 | 136 | setComponentEvent(newFormat, component); 137 | 138 | // component.setText(newFormat.message); 139 | component.addExtra(MessageUtil.newTextComponent(newFormat.message)); 140 | return component; 141 | } 142 | 143 | //使用发送玩家名来处理format 144 | private MessageFormat handlePlayerName(MessageFormat format){ 145 | return handlePlayerName(format,fromPlayer.playerName); 146 | } 147 | 148 | //使用指定玩家名来处理format 149 | private MessageFormat handlePlayerName(MessageFormat format,String name){ 150 | MessageFormat newFormat = new MessageFormat(); 151 | newFormat.message = format.message.replaceAll("\\{displayName}",name); 152 | if (format.hover!=null && !"".equals(format.hover)) 153 | newFormat.hover = format.hover.replaceAll("\\{displayName}",name); 154 | if (format.click!=null && !"".equals(format.click)) 155 | newFormat.click = format.click.replaceAll("\\{displayName}",name); 156 | return newFormat; 157 | } 158 | 159 | //指定发送者和接收者来处理format 160 | private MessageFormat handlePlayerName(MessageFormat format,String fromPlayer,String toPlayer){ 161 | MessageFormat newFormat = new MessageFormat(); 162 | newFormat.message = format.message.replaceAll("\\{formPlayer}",fromPlayer); 163 | newFormat.message = newFormat.message.replaceAll("\\{toPlayer}",toPlayer); 164 | if (format.hover!=null && !"".equals(format.hover)){ 165 | newFormat.hover = format.hover.replaceAll("\\{formPlayer}",fromPlayer); 166 | newFormat.hover = newFormat.hover.replaceAll("\\{toPlayer}",toPlayer); 167 | } 168 | if (format.click!=null && !"".equals(format.click)){ 169 | newFormat.click = format.click.replaceAll("\\{formPlayer}",fromPlayer); 170 | newFormat.click = newFormat.click.replaceAll("\\{toPlayer}",toPlayer); 171 | } 172 | return newFormat; 173 | } 174 | 175 | //给component设置event(hover和click) 176 | private void setComponentEvent(MessageFormat format, TextComponent text) { 177 | format.message = MessageUtil.replace(format.message); 178 | if (null!=format.hover && !"".equals(format.hover)){ 179 | format.hover = MessageUtil.replace(format.hover); 180 | setHover(text,format.hover); 181 | } 182 | if (null!=format.click && !"".equals(format.click)){ 183 | // format.click = MessageUtil.replace(format.click); 184 | setClick(text,format.click); 185 | } 186 | } 187 | 188 | //给component设置hover 189 | public void setHover(TextComponent component,String hover){ 190 | HoverEvent event = new HoverEvent(HoverEvent.Action.SHOW_TEXT,new BaseComponent[]{MessageUtil.newTextComponent(hover)}); 191 | component.setHoverEvent(event); 192 | } 193 | 194 | //给component设置click 195 | public void setClick(TextComponent component,String click){ 196 | Pattern pattern = Pattern.compile(config.linkRegex); 197 | Matcher matcher = pattern.matcher(click); 198 | ClickEvent.Action action; 199 | if (matcher.find()){ 200 | action = ClickEvent.Action.OPEN_URL; 201 | }else { 202 | if (click.startsWith("!")){ 203 | click = click.replaceFirst("^!",""); 204 | action = ClickEvent.Action.RUN_COMMAND; 205 | }else { 206 | action = ClickEvent.Action.SUGGEST_COMMAND; 207 | } 208 | } 209 | ClickEvent event = new ClickEvent(action,click); 210 | component.setClickEvent(event); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/struct/ChatPlayer.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.struct; 2 | 3 | import io.netty.channel.Channel; 4 | import org.lintx.plugins.yinwuchat.bungee.config.PlayerConfig; 5 | 6 | public class ChatPlayer { 7 | public PlayerConfig.Player config; 8 | public String playerName; 9 | public String redisPlayerName = null; 10 | public Channel channel; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/struct/ChatSource.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.struct; 2 | 3 | public enum ChatSource { 4 | GAME, 5 | WEB, 6 | QQ 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/struct/ChatStruct.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.struct; 2 | 3 | import net.md_5.bungee.api.chat.BaseComponent; 4 | 5 | public class ChatStruct { 6 | public String chat; 7 | public BaseComponent component; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/chat/struct/ChatType.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.chat.struct; 2 | 3 | public enum ChatType { 4 | PUBLIC, 5 | PRIVATE 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/json/HandleConfig.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.json; 2 | 3 | import org.lintx.plugins.modules.configure.YamlConfig; 4 | 5 | import java.util.List; 6 | 7 | public class HandleConfig { 8 | @YamlConfig 9 | public String placeholder; 10 | 11 | @YamlConfig 12 | public List format; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/json/Message.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.json; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class Message { 7 | public String player = ""; 8 | public String chat = ""; 9 | public List items = null; 10 | public List handles = new ArrayList<>(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/json/MessageFormat.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.json; 2 | 3 | import org.lintx.plugins.modules.configure.YamlConfig; 4 | 5 | public class MessageFormat { 6 | public MessageFormat(){ 7 | 8 | } 9 | public MessageFormat(String message){ 10 | this(message,null,null); 11 | } 12 | 13 | public MessageFormat(String message,String hover,String click){ 14 | this.message = message; 15 | this.hover = hover; 16 | this.click = click; 17 | } 18 | 19 | @YamlConfig 20 | public String message = null; 21 | 22 | @YamlConfig 23 | public String hover = null; 24 | 25 | @YamlConfig 26 | public String click = null; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/json/PrivateMessage.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.json; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class PrivateMessage extends Message { 7 | //me -> toplayer > message 8 | public List toFormat = new ArrayList<>(); 9 | //toplayer -> me > message 10 | public List fromFormat = new ArrayList<>(); 11 | public String toPlayer = ""; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/lintx/plugins/yinwuchat/json/PublicMessage.java: -------------------------------------------------------------------------------- 1 | package org.lintx.plugins.yinwuchat.json; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class PublicMessage extends Message { 7 | public List format = new ArrayList<>(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: ${artifactId} 2 | main: org.lintx.plugins.yinwuchat.bungee.YinwuChat 3 | version: ${version} 4 | author: LinTx -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ${artifactId} 2 | main: org.lintx.plugins.yinwuchat.bukkit.YinwuChat 3 | version: ${version} 4 | author: LinTx 5 | api-version: "1.13" 6 | depend: [] 7 | softdepend: [PlaceholderAPI] 8 | commands: 9 | msg: 10 | aliases: 11 | - tell 12 | - t 13 | - m 14 | description: Send a private message 15 | usage: / 16 | yinwuchat-bukkit: 17 | description: yinwuchat-bukkit command 18 | usage: / 19 | permissions: 20 | yinwuchat.*: 21 | default: op 22 | children: 23 | yinwuchat.reload: 24 | default: op 25 | yinwuchat.style: 26 | default: op 27 | children: 28 | yinwuchat.style.0: 29 | default: true 30 | yinwuchat.style.1: 31 | default: true 32 | yinwuchat.style.2: 33 | default: true 34 | yinwuchat.style.3: 35 | default: true 36 | yinwuchat.style.4: 37 | default: true 38 | yinwuchat.style.5: 39 | default: true 40 | yinwuchat.style.6: 41 | default: true 42 | yinwuchat.style.7: 43 | default: true 44 | yinwuchat.style.8: 45 | default: true 46 | yinwuchat.style.9: 47 | default: true 48 | yinwuchat.style.a: 49 | default: true 50 | yinwuchat.style.b: 51 | default: true 52 | yinwuchat.style.c: 53 | default: true 54 | yinwuchat.style.d: 55 | default: true 56 | yinwuchat.style.e: 57 | default: true 58 | yinwuchat.style.f: 59 | default: true 60 | yinwuchat.style.k: #随机 61 | default: op 62 | yinwuchat.style.l: #粗体 63 | default: op 64 | yinwuchat.style.m: #删除线 65 | default: op 66 | yinwuchat.style.n: #下划线 67 | default: op 68 | yinwuchat.style.o: #斜体 69 | default: op 70 | yinwuchat.style.r: #重置 71 | default: true 72 | yinwuchat.style.rgb: #RGB颜色 73 | default: op -------------------------------------------------------------------------------- /web-source/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yinwuchat-web", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "private": true, 7 | "scripts": { 8 | "start": "webpack --env.development", 9 | "build": "webpack --env.production" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/core": "^7.4.4", 15 | "@babel/preset-env": "^7.4.4", 16 | "autoprefixer": "^9.5.1", 17 | "babel-loader": "^8.0.5", 18 | "bootstrap-vue": "^2.0.2", 19 | "css-loader": "^2.1.1", 20 | "cssnano": "^4.1.10", 21 | "file-loader": "^3.0.1", 22 | "html-webpack-plugin": "^4.0.0-beta.5", 23 | "html-withimg-loader": "^0.1.16", 24 | "mini-css-extract-plugin": "^0.6.0", 25 | "node-sass": "^4.12.0", 26 | "optimize-css-assets-webpack-plugin": "^5.0.1", 27 | "portal-vue": "^2.1.6", 28 | "postcss-import": "^12.0.1", 29 | "postcss-loader": "^3.0.0", 30 | "sass-loader": "^7.1.0", 31 | "style-loader": "^0.23.1", 32 | "url-loader": "^1.1.2", 33 | "vue": "^2.6.10", 34 | "webpack": "^4.30.0", 35 | "webpack-cleanup-plugin": "^0.5.1", 36 | "webpack-cli": "^3.3.1", 37 | "webpack-plugin-hash-output": "^3.2.1" 38 | }, 39 | "dependencies": { 40 | "bootstrap": "^4.3.1", 41 | "font-awesome": "^4.7.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /web-source/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | YinwuChat 8 | 9 | 10 | 11 | 12 | 13 | YinwuChat 14 | 15 | 16 | 在线玩家 17 | 18 | 19 | 20 | 21 | 22 | 23 | 欢迎来到YinwuChat 24 | 在这里你可以接收到游戏内玩家的消息,绑定token后可以在这里向游戏内发送消息 25 | 在聊天框输入/msg 玩家名 消息向玩家发送私聊消息 26 | YinwuChat Author:LinTx 27 | --- 28 | 29 | 30 | 31 | [错误信息] 32 | [提示信息] 33 | [提示信息] 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 在线玩家 51 | 点击玩家名可以向玩家发送私聊消息 52 | 53 | 54 | 游戏在线 [{{player_list.game.length}}] 55 | 56 | 57 | [{{player.server_name}}] {{player.player_name}} 58 | 59 | 60 | YinwuChat在线 [{{player_list.web.length}}] 61 | 62 | 63 | {{player}} 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /web-source/src/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import "./index.scss" 3 | 4 | const protocol = location.protocol.toLocaleLowerCase() === "https:" ? "wss" : "ws"; 5 | const wsurl = protocol +"://" + location.host + "/ws"; 6 | const msg_type_err = 2,msg_type_info = 1,msg_type_default = 0,msg_type_server = 3; 7 | function formatMessageHtmlObj(message, type) { 8 | let obj = {message:message}; 9 | switch (type) { 10 | case 2: 11 | obj.type = "error"; 12 | break; 13 | case 1: 14 | obj.type = "info"; 15 | break; 16 | case 3: 17 | obj.type = "server"; 18 | break; 19 | default: 20 | obj.type = "message"; 21 | break; 22 | } 23 | return obj; 24 | } 25 | 26 | const app = new Vue({ 27 | el:"#app", 28 | data:{ 29 | message:[], 30 | chat:{ 31 | text:"", 32 | history:[], 33 | historyIndex:0 34 | }, 35 | login:false, 36 | player_list : { 37 | game:[], 38 | web:[] 39 | }, 40 | setting : { 41 | show_time:true, 42 | show_player_list:false 43 | } 44 | }, 45 | methods:{ 46 | onchat(){ 47 | if (this.chat.text.length === 0) { 48 | return; 49 | } 50 | this.chat.history.push(this.chat.text); 51 | if (this.chat.history.length>100){ 52 | this.chat.history.shift(); 53 | } 54 | this.chat.historyIndex = this.chat.history.length; 55 | if (this.login) { 56 | sendMessage(this.chat.text); 57 | } else { 58 | addMessage("你还没有连接到服务器,或者token校验失败,或者token尚未绑定,暂时无法发送消息",msg_type_err); 59 | } 60 | this.chat.text = ""; 61 | }, 62 | chatKeyUp (ev){ 63 | if (ev.ctrlKey || ev.shiftKey || ev.altKey) { 64 | return; 65 | } 66 | if (ev.which === 38) { 67 | if (this.chat.historyIndex > this.chat.history.length){ 68 | this.chat.historyIndex = this.chat.history.length; 69 | } 70 | this.chat.historyIndex -= 1; 71 | this.chat.text = this.chat.history[this.chat.historyIndex]; 72 | } 73 | else if (ev.which === 40) { 74 | if (this.chat.historyIndex < -1){ 75 | this.chat.historyIndex = -1; 76 | } 77 | this.chat.historyIndex += 1; 78 | this.chat.text = this.chat.history[this.chat.historyIndex]; 79 | } 80 | }, 81 | setMsgCmd (player) { 82 | this.chat.text = "/msg " + player + " "; 83 | } 84 | } 85 | }); 86 | 87 | function addMessage(message, type) { 88 | app.message.push(formatMessageHtmlObj(message,type)); 89 | if (document.scrollingElement.scrollHeight - document.scrollingElement.scrollTop - document.documentElement.clientHeight <= 100){ 90 | app.$nextTick(()=>{ 91 | document.scrollingElement.scrollTop = document.scrollingElement.scrollHeight; 92 | }); 93 | } 94 | } 95 | function insMessage(message, type) { 96 | app.message.splice(0,0,formatMessageHtmlObj(message,type)); 97 | } 98 | 99 | function notWebSocket() { 100 | addMessage("看起来你的浏览器不支持WebSocket,YinwuChat的运行依赖于WebSocket,你需要一个支持WebSocket的浏览器,比如Chrome,才能正常使用。",msg_type_err); 101 | } 102 | 103 | if (typeof WebSocket !== "function" && typeof MozWebSocket !== "function") { 104 | notWebSocket(); 105 | } 106 | 107 | let ws; 108 | 109 | const WsHelper = { 110 | timeout:2000, 111 | heardCheckTimeout:60000, 112 | heardCheckTimeoutObj: null, 113 | heardCheckReset: function(){ 114 | clearInterval(this.heardCheckTimeoutObj); 115 | this.heardCheckStart(); 116 | }, 117 | heardCheckStart: function(){ 118 | this.heardCheckTimeoutObj = setInterval(function(){ 119 | if(ws.readyState===1){ 120 | ws.send("HeartBeat"); 121 | } 122 | }, this.heardCheckTimeout) 123 | }, 124 | 125 | lockReconnect:false, 126 | start:function () { 127 | const self = this; 128 | if (this.lockReconnect) return; 129 | this.lockReconnect = true; 130 | setTimeout(()=>{ 131 | self.lockReconnect = false; 132 | self.create(); 133 | },this.timeout); 134 | }, 135 | create:function () { 136 | try { 137 | if ('WebSocket' in window){ 138 | ws = new WebSocket(wsurl); 139 | } 140 | else if ('MozWebSocket' in window) { 141 | ws = new MozWebSocket(wsurl); 142 | } 143 | this.bindEvent(); 144 | } 145 | catch (e) { 146 | notWebSocket(); 147 | this.start(); 148 | } 149 | }, 150 | bindEvent:function () { 151 | const self = this; 152 | ws.onopen = function(){ 153 | addMessage("连接服务器成功,正在校验token",msg_type_info); 154 | sendCheckToken(getToken()); 155 | self.heardCheckStart(); 156 | }; 157 | 158 | ws.onmessage = function(e){ 159 | self.heardCheckReset(); 160 | let json = e.data; 161 | try { 162 | let data = JSON.parse(json); 163 | switch (data.action) { 164 | case "update_token": 165 | updateToken(data.token); 166 | break; 167 | case "check_token": 168 | checkToken(data.status,data.isbind,data.message); 169 | break; 170 | case "send_message": 171 | onMessage(data.message); 172 | break; 173 | case "player_join": 174 | case "player_leave": 175 | case "player_switch_server": 176 | onPlayerStatusMessage(data.player,data.server,data.action); 177 | break; 178 | case "player_web_join": 179 | case "player_web_leave": 180 | onWebPlayerStatusMessage(data.player,data.action); 181 | break; 182 | case "server_message": 183 | onServerMessage(data.message,data.status); 184 | break; 185 | case "game_player_list": 186 | app.player_list.game = data.player_list; 187 | break; 188 | case "web_player_list": 189 | app.player_list.web = data.player_list; 190 | break; 191 | } 192 | } 193 | catch (e) { 194 | console.error(e); 195 | } 196 | }; 197 | ws.onclose = function(e){ 198 | addMessage("WebSocket断开了连接",msg_type_info); 199 | }; 200 | ws.onerror = function (err) { 201 | }; 202 | } 203 | }; 204 | 205 | 206 | addMessage("正在连接服务器",msg_type_info); 207 | WsHelper.create(); 208 | 209 | function getToken(){ 210 | let token = localStorage.getItem("yinwuchat_token"); 211 | if (typeof token !== "string") { 212 | token = ""; 213 | } 214 | return token; 215 | } 216 | 217 | function saveToken(token){ 218 | localStorage.setItem("yinwuchat_token",token); 219 | } 220 | 221 | function sendCheckToken(token){ 222 | let obj = { 223 | action:"check_token", 224 | token:token 225 | }; 226 | ws.send(JSON.stringify(obj)); 227 | } 228 | 229 | function sendMessage(message,status){ 230 | message = message.replace(/&([0-9abcdef])([^&]*)/ig, (regex, color, msg) => { 231 | return "§" + color + msg; 232 | }); 233 | 234 | message = message.replace(/&([klmnor])([^&]*)/ig, (regex, style, msg) => { 235 | return msg; 236 | }); 237 | 238 | message = message.replace(/§([klmnor])([^§]*)/ig, (regex, style, msg) => { 239 | return msg; 240 | }); 241 | 242 | let obj = { 243 | action:"send_message", 244 | message:message 245 | }; 246 | ws.send(JSON.stringify(obj)); 247 | } 248 | 249 | function addBindMsg(token) { 250 | addMessage("请进入游戏,并在游戏内输入命令/yinwuchat bind " + token + "以绑定token。",msg_type_info); 251 | } 252 | 253 | function updateToken(token){ 254 | saveToken(token); 255 | addBindMsg(token); 256 | } 257 | 258 | function checkToken(status,isbind,message){ 259 | if (!status) { 260 | addMessage(message,msg_type_err); 261 | } 262 | else { 263 | if (isbind) { 264 | addMessage("token校验成功,你现在可以发送消息到游戏内了",msg_type_info); 265 | app.login = true; 266 | } 267 | else { 268 | addBindMsg(getToken()); 269 | } 270 | } 271 | } 272 | 273 | function onMessage(message){ 274 | message = formatMessage(message); 275 | addMessage(message,msg_type_default); 276 | } 277 | 278 | // function getClickPlayer(player) { 279 | // return ""+player+"" 280 | // } 281 | 282 | function onServerMessage(message,status){ 283 | message = formatMessage(message); 284 | if (status === 1001) { 285 | insMessage(message,msg_type_server); 286 | } else { 287 | addMessage(message,msg_type_server); 288 | } 289 | } 290 | 291 | function formatMessage(message) { 292 | message = message.replace(/&([0-9abcdefklmnor])([^&]*)/ig, (regex, style, msg) => { 293 | return "§" + style + msg; 294 | }); 295 | 296 | // message = message.replace(/&([klmnor])([^&]*)/ig, (regex, style, msg) => { 297 | // return msg; 298 | // }); 299 | 300 | // message = message.replace(/§([klmnor])([^§]*)/ig, (regex, style, msg) => { 301 | // return msg; 302 | // }); 303 | 304 | message = message.replace(/§([0-9abcdef])([^§]*)/ig, (regex, color, msg) => { 305 | //msg = msg.replace(/ /g, ' '); 306 | return `${msg}`; 307 | }); 308 | 309 | message = message.replace(/§([klmnor])([^§]*)/ig, (regex, style, msg) => { 310 | //msg = msg.replace(/ /g, ' '); 311 | return `${msg}`; 312 | }); 313 | return message; 314 | } 315 | 316 | function onPlayerStatusMessage(player,server,status){ 317 | let message = ""; 318 | switch (status) { 319 | case "player_join": 320 | message = "§6玩家§e" + player + "§6"; 321 | message += "加入了游戏"; 322 | if (server.length > 0) { 323 | message += ",所在服务器:§b" + server; 324 | } 325 | break; 326 | case "player_leave": 327 | message = "§6玩家§e" + player + "§6"; 328 | message += "退出了游戏"; 329 | break; 330 | case "player_switch_server": 331 | message = "§6玩家§e" + player + "§6"; 332 | message += "加入了服务器:§b" + server; 333 | break; 334 | } 335 | message = formatMessage(message); 336 | addMessage(message,msg_type_default); 337 | } 338 | 339 | function onWebPlayerStatusMessage(player,status){ 340 | let message = ""; 341 | switch (status) { 342 | case "player_web_join": 343 | message = "§6玩家§e" + player + "§6"; 344 | message += "加入了YinwuChat"; 345 | break; 346 | case "player_web_leave": 347 | message = "§6玩家§e" + player + "§6"; 348 | message += "离开了YinwuChat"; 349 | break; 350 | } 351 | message = formatMessage(message); 352 | addMessage(message,msg_type_default); 353 | } -------------------------------------------------------------------------------- /web-source/src/index.scss: -------------------------------------------------------------------------------- 1 | @import "~bootstrap"; 2 | 3 | body{ 4 | background: #444444; 5 | word-break: break-all; 6 | } 7 | .yinwuchat-content{ 8 | padding-top: 57px; 9 | padding-bottom: 40px; 10 | color: #D6D6D6; 11 | 12 | .yinwuchat-content-warp{ 13 | padding: 8px; 14 | p{ 15 | margin-bottom: 0; 16 | 17 | .color-a { color: #50FF38; } 18 | .color-b { color: #5BFFFF; } 19 | .color-c { color: #EA3A3F; } 20 | .color-d { color: #EF1DFF; } 21 | .color-e { color: #FAFF34; } 22 | .color-f { color: #EDEDED; } 23 | .color-0 { color: #000000; } 24 | .color-1 { color: #19009C; } 25 | .color-2 { color: #30A000; } 26 | .color-3 { color: #379C9A; } 27 | .color-4 { color: #9A0000; } 28 | .color-5 { color: #99009C; } 29 | .color-6 { color: #EF9B00; } 30 | .color-7 { color: #9A9A9A; } 31 | .color-8 { color: #43443E; } 32 | .color-9 { color: #412BFF; } 33 | .yinwuchat-chat-style-l{font-weight: bold;} 34 | .yinwuchat-chat-style-m{text-decoration: line-through;} 35 | .yinwuchat-chat-style-n{text-decoration: underline;} 36 | .yinwuchat-chat-style-o{font-style: italic;} 37 | .yinwuchat-chat-style-r{font-style: normal;font-weight: normal;color: #EDEDED;text-decoration: none;} 38 | 39 | a { 40 | text-decoration: none; 41 | color: inherit; 42 | } 43 | 44 | a:hover { 45 | color: inherit; 46 | } 47 | } 48 | } 49 | } 50 | .yinwuchat-message-time{ 51 | color: #888888; 52 | } 53 | .yinwuchat-chat{ 54 | height: 40px; 55 | 56 | form{ 57 | width: 100%; 58 | height: 100%; 59 | 60 | input{ 61 | background: transparent; 62 | border: 0; 63 | outline: 0; 64 | padding: 0 8px; 65 | color: #D6D6D6; 66 | width: 100%; 67 | height: 100%; 68 | } 69 | } 70 | } 71 | 72 | .cursor-hand{ 73 | cursor: pointer; 74 | } 75 | 76 | .yinwuchat-player-list{ 77 | top: 57px; 78 | left: 0; 79 | right: 0; 80 | bottom: 40px; 81 | 82 | .row{ 83 | height: 100%; 84 | } 85 | .left{ 86 | background: #000000; 87 | opacity: 0.4; 88 | } 89 | .right{ 90 | background: #444444; 91 | color: white; 92 | overflow-y: scroll; 93 | -webkit-overflow-scrolling: touch; 94 | max-height: 100%; 95 | 96 | h6{ 97 | color: #cccccc; 98 | } 99 | .server-info{ 100 | color: #5BFFFF; 101 | } 102 | .player_name{ 103 | color: #FAFF34; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /web-source/webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const WebpackCleanupPlugin = require('webpack-cleanup-plugin'); 6 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 7 | const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); 8 | 9 | const host = { 10 | dev:'', 11 | dis:'' 12 | }; 13 | 14 | //目录配置 15 | const paths = { 16 | root:{ 17 | //开发环境输出根目录 18 | dev:path.resolve(__dirname, '../../../minecraft.node.js/bungeecord-server/plugins/YinwuChat/web'), 19 | //生产环境输出根目录 20 | dis:path.resolve(__dirname, '../src/main/resources/web'), 21 | //源码文件根目录 22 | src:path.resolve(__dirname, './src') 23 | }, 24 | outpath:{ 25 | //输出的js文件目录 26 | js:'static/', 27 | //输出的图片文件目录 28 | img:'static/images/', 29 | //输出的字体文件目录 30 | font:'static/fonts/', 31 | //输出的css文件目录 32 | css:'static/css/', 33 | } 34 | }; 35 | 36 | module.exports = function (env, argv) { 37 | let mode = 'development'; 38 | let development = true; 39 | if (typeof env === 'object' && env.hasOwnProperty('production') && env.production === true) { 40 | mode = 'production'; 41 | development = false; 42 | } 43 | let publicPath = development?host.dev:host.dis; 44 | let webpackConfig = { 45 | // mode: 'none',//"production" | "development" | "none" 46 | mode: mode, 47 | entry: {},//具体内容由后面编写的脚本填充 48 | output: { 49 | //输出文件根目录,绝对路径 50 | path: development?paths.root.dev:paths.root.dis, 51 | 52 | //输出文件名 53 | filename: paths.outpath.js+'[name].js', 54 | 55 | //sourcemap输出文件名 56 | //[file]:生成后的js文件名(包括路径),[filebase]:生成后的js文件名(不包括路径) 57 | sourceMapFilename: '[file].map', 58 | 59 | //chunk文件输出文件名 60 | chunkFilename:paths.outpath.js+'common.[id].js', 61 | 62 | // publicPath: 相对目录,可以使用cdn地址(开发环境不设置等) 63 | publicPath:publicPath 64 | }, 65 | resolve: { 66 | alias: { 67 | 'vue$': 'vue/dist/vue.esm.js' // 用 webpack 1 时需用 'vue/dist/vue.common.js' 68 | } 69 | }, 70 | //优化选项 71 | optimization:{ 72 | splitChunks:{ 73 | cacheGroups:{ 74 | //公共模块配置 75 | commons:{ 76 | //对导入方式的设置,'async':只管动态导入的,'initial':只管静态导入的,'all':所有的 77 | //静态导入:import 'xxx',动态导入:import('xxx') 78 | chunks: 'all', 79 | //有2个chunk使用才切分到公共文件中 80 | minChunks:10, 81 | //最大并发数,简单理解为一个entry及包含的文件最多被拆分成多少个chunk 82 | maxInitialRequests:5, 83 | //0以上的大小就会切分chunk 84 | minSize:0, 85 | // name:'[chunkhash]'//影响文件名、sourcemap文件名、其他地方引用的chunk的name 86 | // filename:paths.outpath.js+'common.js' 87 | }, 88 | vendor:{ 89 | test:/[\\/]node_modules[\\/]/i, 90 | chunks:'all', 91 | priority:10, 92 | enforce:true 93 | } 94 | } 95 | } 96 | }, 97 | module: { 98 | rules: [ 99 | { 100 | //对ES6语法进行编译 101 | test: /\.js$/i, 102 | exclude:/[\\/]node_modules[\\/]/i, 103 | loader:'babel-loader', 104 | options:{ 105 | presets:['@babel/preset-env'] 106 | } 107 | }, 108 | { 109 | //css文件的处理 110 | test: /\.(css|scss)$/i, 111 | use : [ 112 | //使用插件将css文件提取为单独的文件 113 | MiniCssExtractPlugin.loader, 114 | 115 | //css解析需要的配置 116 | 'css-loader', 117 | 118 | // 处理CSS压缩、@import等需要的配置 119 | { 120 | loader: 'postcss-loader', 121 | options: { 122 | plugins: [ 123 | require('postcss-import')(), 124 | //自动添加fixer(比如--webkit-等) 125 | require('autoprefixer')({ 126 | browsers: ['last 30 versions', "> 2%", "Firefox >= 10", "ie 6-11"] 127 | }) 128 | ] 129 | } 130 | }, 131 | 132 | //处理sass 133 | 'sass-loader', 134 | ] 135 | }, 136 | { 137 | //css中图片的处理 138 | test: /\.(png|svg|jpg|gif)$/i, 139 | use:[ 140 | { 141 | //使用urlloader将图片自动转换成base64/文件 142 | loader:'url-loader', 143 | options:{ 144 | //文件名 145 | name:paths.outpath.img+'[name].[ext]', 146 | 147 | //单独的publicpath 148 | publicPath:publicPath, 149 | // outputPath:path.resolve(__dirname, development?'./output/dev':'./output/dis'), 150 | 151 | //base64/文件的文件大小界限(K) 152 | limit:200 153 | } 154 | } 155 | ] 156 | }, 157 | { 158 | test:/\.(woff|woff2|eot|ttf|otf|svg)$/i, 159 | use:[ 160 | { 161 | loader:'file-loader', 162 | options:{ 163 | name:paths.outpath.font+'/[name].[ext]', 164 | publicPath:publicPath, 165 | limit: 0 166 | } 167 | } 168 | ] 169 | }, 170 | { 171 | //处理html文件中的资源文件,比如图片,提取后匹配上面的图片test,然后由urlloader处理 172 | test:/\.(html)$/i, 173 | use:['html-withimg-loader'] 174 | } 175 | ] 176 | }, 177 | plugins: [ 178 | //css单独打包插件 179 | new MiniCssExtractPlugin({filename:paths.outpath.css+'/[id].css'}), 180 | 181 | //打包前用于清空 output 目录 182 | new WebpackCleanupPlugin(), 183 | ], 184 | // devtool 更详细的资料:https://segmentfault.com/a/1190000008315937 185 | devtool: development ? 'inline-cheap-module-source-map' : false 186 | }; 187 | if (!development) { 188 | //生产模式配置CSS压缩 189 | webpackConfig.plugins.push(new OptimizeCSSAssetsPlugin({ 190 | assetNameRegExp: /\.css$/g, 191 | cssProcessor: require('cssnano'), 192 | // cssProcessorOptions: cssnanoOptions, 193 | cssProcessorPluginOptions: { 194 | preset: ['default', { 195 | discardComments: { 196 | removeAll: true, 197 | }, 198 | normalizeUnicode: false 199 | }] 200 | }, 201 | canPrint: true 202 | })); 203 | } 204 | 205 | webpackConfig.entry["index"] = "./src/index.js"; 206 | webpackConfig.plugins.push(new HtmlWebpackPlugin({ 207 | //输出文件名 208 | filename: "index.html", 209 | 210 | //模板文件(源文件) 211 | template: "./src/index.html", 212 | 213 | //插入的内容在哪里(true|'head'|'body'|false) 214 | inject: true, 215 | 216 | //是否闭合link标签(xhtml标准) 217 | xhtml: true, 218 | 219 | //在插入的js、css标签后面加上hash 220 | hash: true, 221 | 222 | //这个html文件中插入的chunks,3.x版本的插件不能处理common chunk,4.x版本可以自动处理 223 | chunks: ["index"] 224 | })); 225 | 226 | return webpackConfig; 227 | }; --------------------------------------------------------------------------------
欢迎来到YinwuChat
在这里你可以接收到游戏内玩家的消息,绑定token后可以在这里向游戏内发送消息
在聊天框输入/msg 玩家名 消息向玩家发送私聊消息
YinwuChat Author:LinTx
---
31 | [错误信息] 32 | [提示信息] 33 | [提示信息] 34 | 35 |