├── .github └── workflows │ └── Upload Release Asset.yml ├── .gitignore ├── README.md ├── pom.xml ├── settings.xml ├── yuanluServerDo-bukkit-bungeecord └── pom.xml ├── yuanluServerDo-bukkit-velocity └── pom.xml ├── yuanluServerDo-bukkit ├── pom.xml └── src │ └── main │ ├── java │ └── yuan │ │ └── plugins │ │ └── serverDo │ │ └── bukkit │ │ ├── Core.java │ │ ├── MESSAGE.java │ │ ├── Main.java │ │ ├── PackageUtil.java │ │ ├── SafeLoc.java │ │ ├── cmds │ │ ├── Cmd.java │ │ ├── CmdBack.java │ │ ├── CmdDelHome.java │ │ ├── CmdDelSpawn.java │ │ ├── CmdDelWarp.java │ │ ├── CmdHome.java │ │ ├── CmdReload.java │ │ ├── CmdSetHome.java │ │ ├── CmdSetSpawn.java │ │ ├── CmdSetWarp.java │ │ ├── CmdSpawn.java │ │ ├── CmdTp.java │ │ ├── CmdTpa.java │ │ ├── CmdTpaccept.java │ │ ├── CmdTpahere.java │ │ ├── CmdTpcancel.java │ │ ├── CmdTpdeny.java │ │ ├── CmdTphere.java │ │ ├── CmdTrans.java │ │ ├── CmdVanish.java │ │ ├── CmdWarp.java │ │ ├── CommandManager.java │ │ ├── TabHome.java │ │ ├── TabTp.java │ │ ├── TabWarp.java │ │ └── package-info.java │ │ ├── package-info.java │ │ └── third │ │ ├── Third.java │ │ └── package-info.java │ └── resources │ ├── config.yml │ └── plugin.yml ├── yuanluServerDo-bungeecord ├── pom.xml └── src │ └── main │ ├── java │ └── yuan │ │ └── plugins │ │ └── serverDo │ │ └── bungee │ │ ├── ConfigManager.java │ │ ├── Core.java │ │ ├── Main.java │ │ ├── TabHandler.java │ │ ├── TransHandler.java │ │ └── package-info.java │ └── resources │ ├── bungee.yml │ └── proxy-config.yml ├── yuanluServerDo-common ├── pom.xml └── src │ └── main │ └── java │ └── yuan │ └── plugins │ └── serverDo │ ├── At.java │ ├── Channel.java │ ├── ChatColor.java │ ├── LRUCache.java │ ├── ShareData.java │ ├── ShareLocation.java │ ├── Tool.java │ ├── WaitMaintain.java │ └── package-info.java ├── yuanluServerDo-velocity ├── pom.xml └── src │ ├── main │ ├── java │ │ └── yuan │ │ │ └── plugins │ │ │ └── serverDo │ │ │ └── velocity │ │ │ ├── CmdProxy.java │ │ │ ├── ConfigManager.java │ │ │ ├── Core.java │ │ │ ├── Main.java │ │ │ ├── TabHandler.java │ │ │ └── TransHandler.java │ └── resources │ │ ├── proxy-config.yml │ │ └── velocity-plugin.json │ └── test │ └── resources │ └── velocity-plugin.json └── yuanluServerDo.iml /.github/workflows/Upload Release Asset.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: 4 | - "published" 5 | 6 | name: Upload Release Asset 7 | 8 | jobs: 9 | build: 10 | name: Upload Release Asset 11 | runs-on: ubuntu-latest 12 | steps: 13 | 14 | - uses: actions/checkout@v3 15 | 16 | - name: Get release 17 | id: get_release 18 | uses: bruceadams/get-release@v1.2.3 19 | env: 20 | GITHUB_TOKEN: ${{ github.token }} 21 | 22 | - name: get-pom-version 23 | id: pom-version 24 | uses: CptMokoena/maven-get-version-action@main 25 | 26 | - name: Set up JDK 17 27 | uses: actions/setup-java@v3 28 | with: 29 | java-version: '17' 30 | distribution: 'temurin' 31 | cache: maven 32 | 33 | - name: Build with Maven 34 | run: mvn -B package --file pom.xml 35 | 36 | - name: Upload Release Asset (Bukkit) 37 | id: upload-release-asset-bukkit 38 | uses: actions/upload-release-asset@v1 39 | env: 40 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 41 | with: 42 | upload_url: ${{ steps.get_release.outputs.upload_url }} 43 | asset_path: ./yuanluServerDo-bukkit/target/yuanluServerDo-bukkit-${{ steps.pom-version.outputs.version }}.jar 44 | asset_name: yuanluServerDo-bukkit-${{ steps.pom-version.outputs.version }}.jar 45 | asset_content_type: application/java-archive 46 | 47 | - name: Upload Release Asset (Bukkit Bungeecord) 48 | id: upload-release-asset-bukkit-bungeecord 49 | uses: actions/upload-release-asset@v1 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | with: 53 | upload_url: ${{ steps.get_release.outputs.upload_url }} 54 | asset_path: ./yuanluServerDo-bukkit-bungeecord/target/yuanluServerDo-bukkit-bungeecord-${{ steps.pom-version.outputs.version }}.jar 55 | asset_name: yuanluServerDo-bukkit-bungeecord-${{ steps.pom-version.outputs.version }}.jar 56 | asset_content_type: application/java-archive 57 | 58 | - name: Upload Release Asset (Bukkit Velocity) 59 | id: upload-release-asset-bukkit-velocity 60 | uses: actions/upload-release-asset@v1 61 | env: 62 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 63 | with: 64 | upload_url: ${{ steps.get_release.outputs.upload_url }} 65 | asset_path: ./yuanluServerDo-bukkit-velocity/target/yuanluServerDo-bukkit-velocity-${{ steps.pom-version.outputs.version }}.jar 66 | asset_name: yuanluServerDo-bukkit-velocity-${{ steps.pom-version.outputs.version }}.jar 67 | asset_content_type: application/java-archive 68 | 69 | - name: Upload Release Asset (Bungeecord) 70 | id: upload-release-asset-bungeecord 71 | uses: actions/upload-release-asset@v1 72 | env: 73 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 74 | with: 75 | upload_url: ${{ steps.get_release.outputs.upload_url }} 76 | asset_path: ./yuanluServerDo-bungeecord/target/yuanluServerDo-bungeecord-${{ steps.pom-version.outputs.version }}.jar 77 | asset_name: yuanluServerDo-bungeecord-${{ steps.pom-version.outputs.version }}.jar 78 | asset_content_type: application/java-archive 79 | 80 | - name: Upload Release Asset (Velocity) 81 | id: upload-release-asset-velocity 82 | uses: actions/upload-release-asset@v1 83 | env: 84 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 85 | with: 86 | upload_url: ${{ steps.get_release.outputs.upload_url }} 87 | asset_path: ./yuanluServerDo-velocity/target/yuanluServerDo-velocity-${{ steps.pom-version.outputs.version }}.jar 88 | asset_name: yuanluServerDo-velocity-${{ steps.pom-version.outputs.version }}.jar 89 | asset_content_type: application/java-archive 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | 11 | # Compiled class file 12 | *.class 13 | 14 | # Log file 15 | *.log 16 | 17 | # BlueJ files 18 | *.ctxt 19 | 20 | # Package Files # 21 | *.jar 22 | *.war 23 | *.nar 24 | *.ear 25 | *.zip 26 | *.tar.gz 27 | *.rar 28 | 29 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 30 | hs_err_pid* 31 | 32 | *~ 33 | 34 | # temporary files which can be created if a process still has a handle open of a deleted file 35 | .fuse_hidden* 36 | 37 | # KDE directory preferences 38 | .directory 39 | 40 | # Linux trash folder which might appear on any partition or disk 41 | .Trash-* 42 | 43 | # .nfs files are created when an open file is removed but is still being accessed 44 | .nfs* 45 | 46 | # General 47 | .DS_Store 48 | .AppleDouble 49 | .LSOverride 50 | 51 | # Icon must end with two \r 52 | Icon 53 | 54 | # Thumbnails 55 | ._* 56 | 57 | # Files that might appear in the root of a volume 58 | .DocumentRevisions-V100 59 | .fseventsd 60 | .Spotlight-V100 61 | .TemporaryItems 62 | .Trashes 63 | .VolumeIcon.icns 64 | .com.apple.timemachine.donotpresent 65 | 66 | # Directories potentially created on remote AFP share 67 | .AppleDB 68 | .AppleDesktop 69 | Network Trash Folder 70 | Temporary Items 71 | .apdisk 72 | 73 | # Windows thumbnail cache files 74 | Thumbs.db 75 | Thumbs.db:encryptable 76 | ehthumbs.db 77 | ehthumbs_vista.db 78 | 79 | # Dump file 80 | *.stackdump 81 | 82 | # Folder config file 83 | [Dd]esktop.ini 84 | 85 | # Recycle Bin used on file shares 86 | $RECYCLE.BIN/ 87 | 88 | # Windows Installer files 89 | *.cab 90 | *.msi 91 | *.msix 92 | *.msm 93 | *.msp 94 | 95 | # Windows shortcuts 96 | *.lnk 97 | 98 | target/ 99 | 100 | pom.xml.tag 101 | pom.xml.releaseBackup 102 | pom.xml.versionsBackup 103 | pom.xml.next 104 | 105 | release.properties 106 | dependency-reduced-pom.xml 107 | buildNumber.properties 108 | .mvn/timing.properties 109 | .mvn/wrapper/maven-wrapper.jar 110 | .flattened-pom.xml 111 | 112 | # Common working directory 113 | run/ 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yuanluerServerDo 2 | 3 | __插件暂时或永久停更,作者毕业上班了。无法再分出精力维护,有能力者可以提交维护代码__ 4 | 5 | 让跨服玩家无缝执行命令 6 | Allow players on the group server to seamlessly execute commands across servers 7 | 8 | 理论全版本支持 9 | Theory full version support 10 | 11 | ## 交流 12 | 13 | [discord](https://discord.gg/5SZNhTkqJg) 14 | 15 | ## 统计数据 16 | 17 | - [bstats Bukkit](https://bstats.org/plugin/bukkit/yuanluServerDo/12395) 18 | - [bstats Bungee](https://bstats.org/plugin/bungeecord/yuanluServerDo/12396) 19 | - ![最新版本 badge](https://update.yuanlu.bid/ico/v/mc-bukkit/yuanluServerDo "最新版本") 20 | - ![bStats Players](https://img.shields.io/bstats/players/12396?label=%E7%8E%A9%E5%AE%B6%E6%95%B0%E9%87%8F) ![bStats Servers](https://img.shields.io/bstats/servers/12396?label=%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%95%B0%E9%87%8F) 21 | ![bStats Count](https://bstats.org/signatures/bungeecord/yuanluServerDo.svg) 22 | 23 | ## 下载 24 | 25 | __支持服务器: Bukkit (Spigot、Paper)__ 26 | __支持代理器: BungeeCord、Velocity__ 27 | 28 | 同一个版本有多个文件可供选择, 文件名中包含Bukkit的即代表可以在Bukkit及其分支(Spigot、Paper)上运行,其它同理 29 | 例如,你选择使用BungeeCord及Paper,你有以下几种选择: 30 | 31 | 1. 下载`yuanluServerDo-bukkit-[version].jar`及`yuanluServerDo-bungeecord-[version].jar`, 分别放入Paper和BungeeCord 32 | 2. 直接下载`yuanluServerDo-bukkit-bungeecord-[version].jar`放入Paper和BungeeCord 33 | 34 | ## 替代yuanluServerTp 35 | 36 | 本插件继承了yuanluServerTp思想, 重构后可完全替代此插件 37 | 38 | - /tp \ 39 | - /tp \ \ 40 | - /tpa \ 41 | - /tphere \ 42 | - /tpahere \ 43 | - /tpaccept \[who\] 44 | - /tpdeny \[who\] 45 | - /tpcancel \[who\] 46 | 47 | ## 实现了列表隐身 48 | 49 | 使用/ysd-v \[always\] 命令可以隐藏自己的在线状态, 防止被传送请求发现 50 | 51 | ## 实现跨服Home及Warp 52 | 53 | 本插件实现了Home及Warp的相关功能, 可以跨服传送至家或地标 54 | 55 | - /home 56 | - /home \ 57 | - /sethome \[home\] 58 | - /delhome \ 59 | - /warp 60 | - /warp \ 61 | - /setwarp \ 62 | - /delhome \ 63 | 64 | ## 实现第三方数据转换 65 | 66 | 本插件实现了从第三方插件转换数据 67 | 68 | 命令: /ysd-trans \ \ 69 | 70 | 当前支持的插件及数据: 71 | 72 | - CMI 73 | - Home 74 | - Warp 75 | 76 | ## 实现了At功能 77 | 78 | 在聊天中使用@+玩家名, 可以向对方发送提示音 79 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | bid.yuanlu 8 | yuanluServerDo 9 | 1.2.2 10 | YuanluServerDo 11 | Provide players with a series of cross server operations 12 | pom 13 | 14 | 15 | yuanlu 16 | yuanlu 17 | 2573580691@qq.com 18 | 19 | 20 | https://github.com/MineYuanlu/yuanluServerDo 21 | 22 | 1.8 23 | UTF-8 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-compiler-plugin 32 | 3.8.1 33 | 34 | ${java.version} 35 | ${java.version} 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-shade-plugin 41 | 3.2.4 42 | 43 | 44 | 45 | org.bstats 46 | yuan.plugins.serverDo.bstats 47 | 48 | 49 | cn.mapland.yuanlu.updater 50 | yuan.plugins.serverDo.updater 51 | 52 | 53 | 54 | 55 | 56 | package 57 | 58 | shade 59 | 60 | 61 | false 62 | 63 | 64 | *:* 65 | 66 | META-INF/*.SF 67 | META-INF/*.DSA 68 | META-INF/*.RSA 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | org.projectlombok 84 | lombok 85 | 1.18.34 86 | provided 87 | 88 | 89 | 90 | 91 | yl-yuanlu-mcsp-main 92 | https://yl-yuanlu-maven.pkg.coding.net/repository/mcsp/main/ 93 | 94 | true 95 | 96 | 97 | true 98 | 99 | 100 | 101 | 102 | yuanluServerDo-common 103 | yuanluServerDo-bukkit 104 | yuanluServerDo-bungeecord 105 | yuanluServerDo-velocity 106 | yuanluServerDo-bukkit-bungeecord 107 | yuanluServerDo-bukkit-velocity 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ${CODING_MAVEN_REPO_ID} 8 | ${CODING_MAVEN_REG_USERNAME} 9 | ${CODING_MAVEN_REG_PASSWORD} 10 | 11 | 12 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit-bungeecord/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bid.yuanlu 9 | yuanluServerDo 10 | 1.2.2 11 | 12 | 13 | yuanluServerDo-bukkit-bungeecord 14 | jar 15 | 16 | YuanluServerDo Bukkit & BungeeCord 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-shade-plugin 27 | 28 | 29 | 30 | 31 | src/main/resources 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | bid.yuanlu 43 | yuanluServerDo-bukkit 44 | ${project.version} 45 | compile 46 | 47 | 48 | bid.yuanlu 49 | yuanluServerDo-bungeecord 50 | ${project.version} 51 | compile 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit-velocity/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bid.yuanlu 9 | yuanluServerDo 10 | 1.2.2 11 | 12 | 13 | yuanluServerDo-bukkit-velocity 14 | jar 15 | 16 | YuanluServerDo Bukkit & Velocity 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-shade-plugin 27 | 28 | 29 | 30 | 31 | src/main/resources 32 | true 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | bid.yuanlu 43 | yuanluServerDo-bukkit 44 | ${project.version} 45 | compile 46 | 47 | 48 | bid.yuanlu 49 | yuanluServerDo-velocity 50 | ${project.version} 51 | compile 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bid.yuanlu 9 | yuanluServerDo 10 | 1.2.2 11 | 12 | 13 | yuanluServerDo-bukkit 14 | jar 15 | 16 | YuanluServerDo Bukkit 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-shade-plugin 27 | 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | src/main/resources 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | spigotmc-repo 47 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 48 | 49 | 50 | 51 | 52 | 53 | org.bukkit 54 | bukkit 55 | 1.15.1-R0.1-SNAPSHOT 56 | provided 57 | 58 | 59 | bid.yuanlu 60 | yuanluServerDo-common 61 | ${project.version} 62 | compile 63 | 64 | 65 | 66 | org.bstats 67 | bstats-bukkit 68 | 2.2.1 69 | compile 70 | 71 | 72 | 73 | cn.mapland.yuanlu 74 | UpdaterClient-bukkit 75 | 1.0.9 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/MESSAGE.java: -------------------------------------------------------------------------------- 1 | /** 2 | * auto:
3 | * user: yuanlu
4 | * date: 星期三 12 02 2020 5 | */ 6 | package yuan.plugins.serverDo.bukkit; 7 | 8 | import lombok.*; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.command.CommandSender; 11 | import yuan.plugins.serverDo.Tool; 12 | 13 | import java.util.*; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | /** 17 | * 语言文件 18 | * 19 | * @author yuanlu 20 | */ 21 | public interface MESSAGE { 22 | Msg NO_PERMISSION = mes("no-permission"); 23 | Msg CMD_HELP = mes("cmd.help"); 24 | Msg NOT_PLAYER = mes("not-player"); 25 | Msg M_TIME_OUT = mes("basic.message-time-out"); 26 | Msg BAD_VERSION = mes("basic.version-bad"); 27 | Msg BC_ERROR = mes("basic.bungee-error"); 28 | Msg BC_PLAYER_OFF = mes("basic.bungee-player-offline"); 29 | Msg TPCANCEL_MOVE = mes("tpcancel-move"); 30 | Msg VER_NO_RECOMMEND = mes("version-no-recommend"); 31 | 32 | static Msg mes(String node) { 33 | return Main.getMain().mes(node); 34 | } 35 | 36 | static Msg mes(String node, int type) { 37 | return Main.getMain().mes(node, type); 38 | } 39 | 40 | static String strMes(String node, int type) { 41 | return mes(node, type).getMsg(); 42 | } 43 | 44 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 45 | final class EmptyMsg extends MsgReal { 46 | public static final EmptyMsg INSTANCE = new EmptyMsg(); 47 | 48 | @Override 49 | public String getMsg() { 50 | return ""; 51 | } 52 | 53 | @Override 54 | public void send(@NonNull CommandSender sender, @NonNull Map args) { 55 | 56 | } 57 | 58 | @Override 59 | public void send(@NonNull CommandSender sender, Object @NonNull ... args) { 60 | 61 | } 62 | 63 | } 64 | 65 | final class JsonMsg extends MsgReal { 66 | private final @NonNull 67 | @Getter String json, msg; 68 | 69 | private JsonMsg(@NonNull String json, @NonNull String metaMsg) { 70 | this.json = " "/* 减少字符串拼接次数 */ + json; 71 | msg = metaMsg; 72 | } 73 | 74 | static void send(CommandSender sender, String cmd) { 75 | Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "tellraw " + sender.getName() + cmd); 76 | } 77 | 78 | @Override 79 | public void send(@NonNull CommandSender sender, @NonNull Map args) { 80 | String cmd = Tool.parseVar(json, '<', '>', args); 81 | send(sender, cmd); 82 | } 83 | 84 | @Override 85 | public void send(@NonNull CommandSender sender, Object @NonNull ... args) { 86 | String cmd = this.json; 87 | if (args.length != 0) try { 88 | cmd = String.format(cmd, args); 89 | } catch (IllegalArgumentException e) { 90 | Main.getMain().getLogger().warning("错误的格式化: " + cmd + ", 参数: " + Arrays.deepToString(args)); 91 | } 92 | send(sender, cmd); 93 | } 94 | 95 | } 96 | 97 | final class MultiJsonMsg extends MsgReal { 98 | private final @NonNull 99 | @Getter String json; 100 | private final @NonNull 101 | @Getter String msg; 102 | 103 | private MultiJsonMsg(@NonNull List json, @NonNull String metaMsg) { 104 | val sj = new StringJoiner("\0"); 105 | json.stream().map(" "::concat).forEach(sj::add); 106 | this.json = sj.toString(); 107 | msg = metaMsg; 108 | } 109 | 110 | @Override 111 | public void send(@NonNull CommandSender sender, @NonNull Map args) { 112 | String cmd = Tool.parseVar(json, '<', '>', args); 113 | for (val msg : cmd.split("\0")) JsonMsg.send(sender, msg); 114 | } 115 | 116 | @Override 117 | public void send(@NonNull CommandSender sender, Object @NonNull ... args) { 118 | String cmd = this.json; 119 | if (args.length != 0) try { 120 | cmd = String.format(cmd, args); 121 | } catch (IllegalArgumentException e) { 122 | Main.getMain().getLogger().warning("错误的格式化: " + cmd + ", 参数: " + Arrays.deepToString(args)); 123 | } 124 | for (val msg : cmd.split("\0")) JsonMsg.send(sender, msg); 125 | } 126 | 127 | } 128 | 129 | /** 130 | * 消息 131 | * 132 | * @see StrMsg 133 | * @see JsonMsg 134 | */ 135 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 136 | @Getter 137 | final class Msg { 138 | private static final Map MSG_REALS = new ConcurrentHashMap<>(); 139 | 140 | private static final Map MSGS = new ConcurrentHashMap<>(); 141 | private @NonNull 142 | final String key; 143 | 144 | private static Msg cache(String node, int type, MsgReal instance) { 145 | val key = type + "-" + node; 146 | MSG_REALS.put(key, instance); 147 | return MSGS.computeIfAbsent(key, Msg::new); 148 | } 149 | 150 | static Msg get(String node, int type) { 151 | return cache(node, type, EmptyMsg.INSTANCE); 152 | } 153 | 154 | static Msg get(@NonNull String node, int type, @NonNull String msg) { 155 | return cache(node, type, new StrMsg(msg)); 156 | } 157 | 158 | static Msg get(@NonNull String node, int type, @NonNull String json, String metaMsg) { 159 | return cache(node, type, new JsonMsg(json, metaMsg)); 160 | } 161 | 162 | static Msg get(@NonNull String node, int type, @NonNull List json, String metaMsg) { 163 | return cache(node, type, new MultiJsonMsg(json, metaMsg)); 164 | } 165 | 166 | static void reload() { 167 | val msgs = new ArrayList<>(MSGS.values()); 168 | val m = Main.getMain(); 169 | for (val msg : msgs) { 170 | val arg = msg.key.split("-", 2); 171 | m.mes(arg[1], Integer.parseInt(arg[0])); 172 | } 173 | } 174 | 175 | public String getMsg() { 176 | return getReal().getMsg(); 177 | } 178 | 179 | @NonNull 180 | private MsgReal getReal() { 181 | val real = MSG_REALS.get(key); 182 | if (real == null) throw new InternalError("Bad node:" + key); 183 | return real; 184 | } 185 | 186 | @Override 187 | public int hashCode() { 188 | return key.hashCode(); 189 | } 190 | 191 | @Override 192 | public boolean equals(Object obj) { 193 | if (this == obj) return true; 194 | if ((obj == null) || (getClass() != obj.getClass())) return false; 195 | Msg other = (Msg) obj; 196 | return key.equals(other.key); 197 | } 198 | 199 | @Override 200 | public String toString() { 201 | return getMsg(); 202 | } 203 | 204 | /** 205 | * 发送一条消息 206 | * 207 | * @param sender 发送的对象 208 | * @param args 参数 209 | */ 210 | public void send(@NonNull CommandSender sender, @NonNull Map args) { 211 | getReal().send(sender, args); 212 | } 213 | 214 | /** 215 | * 发送一条消息 216 | * 217 | * @param sender 发送的对象 218 | * @param args 参数 219 | */ 220 | public void send(@NonNull CommandSender sender, @NonNull Object @NonNull ... args) { 221 | getReal().send(sender, args); 222 | } 223 | } 224 | 225 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 226 | abstract class MsgReal { 227 | public abstract String getMsg(); 228 | 229 | /** 230 | * 发送一条消息 231 | * 232 | * @param sender 发送的对象 233 | * @param args 参数 234 | */ 235 | public abstract void send(@NonNull CommandSender sender, @NonNull Map args); 236 | 237 | /** 238 | * 发送一条消息 239 | * 240 | * @param sender 发送的对象 241 | * @param args 参数 242 | */ 243 | public abstract void send(@NonNull CommandSender sender, @NonNull Object @NonNull ... args); 244 | 245 | @Override 246 | public String toString() { 247 | return getMsg(); 248 | } 249 | 250 | } 251 | 252 | @AllArgsConstructor(access = AccessLevel.PRIVATE) 253 | final class StrMsg extends MsgReal { 254 | private final @NonNull 255 | @Getter String msg; 256 | 257 | @Override 258 | public void send(@NonNull CommandSender sender, @NonNull Map args) { 259 | String msg = Tool.parseVar(this.msg, '<', '>', args); 260 | sender.sendMessage(msg); 261 | } 262 | 263 | @Override 264 | public void send(@NonNull CommandSender sender, @NonNull Object @NonNull ... args) { 265 | String msg = this.msg; 266 | if (args.length != 0) try { 267 | msg = String.format(msg, args); 268 | } catch (IllegalArgumentException e) { 269 | Main.getMain().getLogger().warning("错误的格式化: " + msg + ", 参数: " + Arrays.deepToString(args)); 270 | } 271 | sender.sendMessage(msg); 272 | } 273 | 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/Main.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo.bukkit; 2 | 3 | import cn.mapland.yuanlu.updater.bukkit.BukkitUpdater; 4 | import lombok.Getter; 5 | import lombok.val; 6 | import org.bstats.bukkit.Metrics; 7 | import org.bstats.charts.MultiLineChart; 8 | import org.bstats.charts.SimplePie; 9 | import org.bukkit.ChatColor; 10 | import org.bukkit.configuration.file.FileConfiguration; 11 | import org.bukkit.configuration.file.YamlConfiguration; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.event.Listener; 14 | import org.bukkit.plugin.Plugin; 15 | import org.bukkit.plugin.java.JavaPlugin; 16 | import yuan.plugins.serverDo.Channel; 17 | import yuan.plugins.serverDo.ShareData; 18 | import yuan.plugins.serverDo.Tool; 19 | import yuan.plugins.serverDo.bukkit.MESSAGE.Msg; 20 | import yuan.plugins.serverDo.bukkit.cmds.Cmd; 21 | import yuan.plugins.serverDo.bukkit.cmds.CommandManager; 22 | 23 | import java.io.*; 24 | import java.nio.charset.StandardCharsets; 25 | import java.util.*; 26 | 27 | /** 28 | * 主类 29 | * 30 | * @author yuanlu 31 | */ 32 | public class Main extends JavaPlugin implements Listener { 33 | 34 | /** 语言文件丢失时显示的信息 模板自动生成 */ 35 | public static final String LANG_LOST = "§c§l[语言文件缺失]§4§l(完全损坏)§c§l节点:%node%"; 36 | 37 | /** 语言文件丢失时显示的信息 模板自动生成 */ 38 | private static String langLost; 39 | 40 | /** 插件前缀 模板自动生成 */ 41 | private static String prefix = ""; 42 | 43 | /** 插件主体 */ 44 | private static @Getter Main main; 45 | 46 | /** 调试模式 */ 47 | private static @Getter boolean DEBUG; 48 | /** 强制替换文件 */ 49 | private static @Getter boolean FORECE_OUT_FILE; 50 | /** 语言缺失的节点 */ 51 | private FileConfiguration MESSAGE_LOST_NODE; 52 | /** 插件配置文件 */ 53 | private @Getter FileConfiguration config; 54 | 55 | /** 56 | * 向玩家(BC端)发送数据 57 | * 58 | * @param player 玩家 59 | * @param data 数据 60 | */ 61 | public static void send(Player player, byte[] data) { 62 | if (isDEBUG()) getMain().getLogger().info("发送: " + player.getName() + " " + Arrays.toString(data)); 63 | player.sendPluginMessage(getMain(), ShareData.BC_CHANNEL, data); 64 | } 65 | 66 | /** 67 | * 翻译字符串
68 | * 将文字中的彩色字符串进行翻译 69 | * 70 | * @param s 字符串 71 | * 72 | * @return 翻译后的字符串 73 | */ 74 | public static String t(String s) { 75 | return ChatColor.translateAlternateColorCodes('&', s); 76 | } 77 | 78 | /** 79 | * bstats数据收集
80 | * 在{@link #onEnable()}调用 81 | */ 82 | private void bstats() { 83 | 84 | // 注册bstats 85 | int pluginId = 12395; 86 | Metrics metrics = new Metrics(this, pluginId); 87 | metrics.addCustomChart(new SimplePie("pls_count", () -> { 88 | int count = 0; 89 | for (Plugin pl : getServer().getPluginManager().getPlugins()) { 90 | if (pl.getName().startsWith("yuanlu")) count++; 91 | } 92 | return Integer.toString(count); 93 | })); 94 | metrics.addCustomChart(new MultiLineChart("cmds", () -> { 95 | HashMap m = new HashMap<>(); 96 | Cmd.EXECUTE_COUNT.forEach((k, v) -> m.put(Cmd.getCmdName(k), v.getAndSet(0))); 97 | return m; 98 | })); 99 | metrics.addCustomChart(new MultiLineChart("packets", Channel::getPackCount)); 100 | } 101 | 102 | /** 检查中央配置文件 */ 103 | private void checkYuanluConfig() { 104 | val yuanluFolder = new File(getDataFolder().getParentFile(), "yuanlu"); 105 | val configFile = new File(yuanluFolder, "config.yml"); 106 | if (!configFile.exists()) { 107 | DEBUG = false; 108 | FORECE_OUT_FILE = false; 109 | } else { 110 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 111 | DEBUG = config.getBoolean("debug", false); 112 | FORECE_OUT_FILE = config.getBoolean("force-override-file", false); 113 | } 114 | } 115 | 116 | /** 117 | * 从配置文件获取信息
118 | * 用于tab列表或lore说明
119 | * 注意: 返回的list均为副本
120 | * 容量为list的大小, 若长期保存, 建议在添加新元素(如果有)后进行长度剪裁({@link ArrayList#trimToSize()}) 121 | * 122 | * @param node 节点 123 | * 124 | * @return 获取到的字符串 125 | */ 126 | public ArrayList list(String node) { 127 | node = "message." + node; 128 | if (config.isList(node)) { 129 | List l = config.getStringList(node); 130 | ArrayList r = new ArrayList<>(l.size()); 131 | l.forEach(x -> r.add(t(x))); 132 | return r; 133 | } else if (config.isString(node)) { 134 | String message = config.getString(node); 135 | List l = Arrays.asList(message.split("\n")); 136 | ArrayList r = new ArrayList<>(l.size()); 137 | l.forEach(x -> r.add(t(x))); 138 | return r; 139 | } else { 140 | getLogger().warning("§d[LMES] §c§lcan not find list in config: " + node); 141 | return new ArrayList<>(Collections.singletonList(langLost.replace("%node%", node))); 142 | } 143 | } 144 | 145 | /** 146 | * 加载配置 147 | * 148 | * @param fileName 配置文件名,例如{@code "config.yml"} 149 | * 150 | * @return 配置文件 151 | */ 152 | public YamlConfiguration loadFile(String fileName) { 153 | File file = new File(getDataFolder(), fileName); 154 | if (FORECE_OUT_FILE || !file.exists()) try { 155 | saveResource(fileName, FORECE_OUT_FILE); 156 | } catch (IllegalArgumentException e) { 157 | try { 158 | file.createNewFile(); 159 | } catch (IOException e1) { 160 | e1.printStackTrace(); 161 | } 162 | } catch (Exception e1) { 163 | e1.printStackTrace(); 164 | return null; 165 | } 166 | try { 167 | return YamlConfiguration.loadConfiguration(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)); 168 | } catch (Exception e) { 169 | e.printStackTrace(); 170 | return null; 171 | } 172 | } 173 | 174 | /** 175 | * @param node 节点 176 | * 177 | * @return 获取到的字符串 178 | * 179 | * @see #mes(String, int) 180 | */ 181 | public Msg mes(String node) { 182 | return mes(node, 0); 183 | } 184 | 185 | /** 186 | * 从配置文件获取信息 187 | *

188 | * type:
189 | * 190 | * 191 | * 192 | * 193 | * 194 | * 195 | * 196 | * 197 | * 198 | * 199 | * 200 | * 201 | * 202 | * 203 | * 204 | * 205 | * 206 | * 207 | * 208 | * 209 | * 210 | *
类型十进制二进制
没有前缀11
检查空字符串210
禁用json模式4100
211 | * 212 | * @param node 节点 213 | * @param type 类型 214 | * 215 | * @return 获取到的字符串 216 | */ 217 | public Msg mes(String node, int type) { 218 | val real = node; 219 | node = "message." + node; 220 | boolean nop = (type & 1) > 0; 221 | boolean checkEmpty = (type & 2) > 0; 222 | boolean notSenior = (type & 4) > 0; 223 | if (config.isConfigurationSection(node)) { 224 | val msg = mes(real + ".msg", type | 4); 225 | if (notSenior) return msg; 226 | else { 227 | val jsonNode = node + ".json"; 228 | if (config.isString(jsonNode)) { 229 | String json = config.getString(jsonNode, null); 230 | if (json != null) return Msg.get(node, type, t(json), msg.getMsg()); 231 | } else if (config.isList(jsonNode)) { 232 | val msgs = config.getStringList(jsonNode); 233 | msgs.replaceAll(Main::t); 234 | return Msg.get(node, type, msgs, msg.getMsg()); 235 | } else return msg; 236 | } 237 | } else if (config.isList(node)) { 238 | List l = config.getStringList(node); 239 | final StringBuilder sb = new StringBuilder(32); 240 | l.forEach(x -> { 241 | if (!nop) sb.append(prefix); 242 | sb.append(x).append('\n'); 243 | }); 244 | if (sb.length() > 0) sb.setLength(sb.length() - 1); 245 | 246 | return sb.length() < 1 ? (checkEmpty ? null : Msg.get(node, type)) : Msg.get(node, type, t(sb.toString())); 247 | } else if (config.isString(node)) { 248 | String message = config.getString(node, ""); 249 | return message.isEmpty() ? (checkEmpty ? null : Msg.get(node, type)) : Msg.get(node, type, t(nop ? message : (prefix + message))); 250 | } 251 | if (MESSAGE_LOST_NODE != null) MESSAGE_LOST_NODE.set(node, node); 252 | getLogger().warning("§d[LMES] §c§lcan not find message in config: " + node); 253 | if (nop) return Msg.get(node, type, t(langLost.replace("%node%", node))); 254 | return Msg.get(node, type, t(prefix + langLost.replace("%node%", node))); 255 | } 256 | 257 | @Override 258 | public void onLoad() { 259 | main = this; 260 | 261 | PackageUtil.setLoader(getClassLoader()); 262 | 263 | ShareData.setLogger(getLogger()); 264 | 265 | checkYuanluConfig(); 266 | ShareData.setDEBUG(DEBUG); 267 | 268 | config = loadFile("config.yml"); 269 | if (config.getBoolean("setting.preload")) { 270 | CommandManager.init(config.getConfigurationSection("cmd")); 271 | } 272 | } 273 | 274 | @Override 275 | public void onDisable() { 276 | // 关闭插件时自动发出 277 | getLogger().info("§a" + ShareData.SHOW_NAME + "-关闭"); 278 | if (ShareData.isDEBUG()) saveFile(MESSAGE_LOST_NODE, "lang-lost.yml"); 279 | } 280 | 281 | @Override 282 | public void onEnable() { 283 | 284 | bstats(); 285 | // update(); 286 | 287 | if (ShareData.isDEBUG()) { 288 | MESSAGE_LOST_NODE = loadFile("lang-lost.yml"); 289 | MESSAGE_LOST_NODE.set("message", loadFile("config.yml").getConfigurationSection("message")); 290 | } 291 | 292 | Tool.load(Channel.class); 293 | 294 | // 启用插件时自动发出 295 | prefix = config.getString("Prefix", ""); 296 | langLost = config.getString("message.LanguageFileIsLost", LANG_LOST); 297 | getServer().getPluginManager().registerEvents(Core.INSTANCE, this); // 注册监听器 298 | 299 | if (!config.getBoolean("setting.preload")) { 300 | CommandManager.init(config.getConfigurationSection("cmd")); 301 | } 302 | 303 | getServer().getMessenger().registerOutgoingPluginChannel(this, ShareData.BC_CHANNEL); 304 | getServer().getMessenger().registerIncomingPluginChannel(this, ShareData.BC_CHANNEL, Core.INSTANCE); 305 | Core.init(config); 306 | 307 | if (Core.Conf.isSafeLocation()) SafeLoc.init(loadFile("blocks.yml")); 308 | 309 | getLogger().info("§a" + ShareData.SHOW_NAME + "-启动"); 310 | } 311 | 312 | /** 重载插件 */ 313 | public void reload() { 314 | val m = this; 315 | m.getServer().getPluginManager().disablePlugin(m); 316 | m.getServer().getPluginManager().enablePlugin(m); 317 | Msg.reload(); 318 | } 319 | 320 | /** 321 | * 保存配置 322 | * 323 | * @param c 配置文件 324 | * @param fileName 保存名称 325 | * 326 | * @author yuanlu 327 | */ 328 | public void saveFile(FileConfiguration c, String fileName) { 329 | val f = new File(getDataFolder(), fileName); 330 | try (val o = new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)) { 331 | o.write(c.saveToString()); 332 | } catch (Throwable e) { 333 | e.printStackTrace(); 334 | } 335 | } 336 | 337 | /** 更新检测 */ 338 | private void update() { 339 | try { 340 | val updater = new BukkitUpdater(this); 341 | updater.useCmd(); 342 | updater.dailyCheck(); 343 | if (updater.getConf().isNoticeUser()) updater.noticeJoin(); 344 | } catch (Throwable e) { 345 | e.printStackTrace(); 346 | } 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/PackageUtil.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo.bukkit; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | 7 | import java.io.File; 8 | import java.net.URL; 9 | import java.net.URLClassLoader; 10 | import java.net.URLDecoder; 11 | import java.util.ArrayList; 12 | import java.util.Enumeration; 13 | import java.util.List; 14 | import java.util.jar.JarEntry; 15 | import java.util.jar.JarFile; 16 | 17 | /** 18 | * 包工具 19 | * 20 | * @author wujf 21 | * @see 原地址 22 | */ 23 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 24 | public class PackageUtil { 25 | 26 | /** 加载器 */ 27 | @Setter 28 | static ClassLoader loader; 29 | 30 | /** 31 | * 获取某包下(包括该包的所有子包)所有类 32 | * 33 | * @param packageName 包名 34 | * 35 | * @return 类的完整名称 36 | */ 37 | public static List getClassName(String packageName) { 38 | return getClassName(packageName, true); 39 | } 40 | 41 | /** 42 | * 获取某包下所有类 43 | * 44 | * @param packageName 包名 45 | * @param childPackage 是否遍历子包 46 | * 47 | * @return 类的完整名称 48 | */ 49 | public static List getClassName(String packageName, boolean childPackage) { 50 | List fileNames = null; 51 | String packagePath = packageName.replace(".", "/"); 52 | URL url = loader.getResource(packagePath); 53 | if (url != null) { 54 | String type = url.getProtocol(); 55 | if (type.equals("file")) { 56 | fileNames = getClassNameByFile(url.getPath(), null, childPackage); 57 | } else if (type.equals("jar")) { 58 | fileNames = getClassNameByJar(url.getPath(), childPackage); 59 | } 60 | } else { 61 | fileNames = getClassNameByJars(((URLClassLoader) loader).getURLs(), packagePath, childPackage); 62 | } 63 | return fileNames; 64 | } 65 | 66 | /** 67 | * 从项目文件获取某包下所有类 68 | * 69 | * @param filePath 文件路径 70 | * @param className 类名集合 71 | * @param childPackage 是否遍历子包 72 | * 73 | * @return 类的完整名称 74 | */ 75 | private static List getClassNameByFile(String filePath, List className, boolean childPackage) { 76 | List myClassName = new ArrayList<>(); 77 | File file = new File(filePath); 78 | File[] childFiles = file.listFiles(); 79 | for (File childFile : childFiles) { 80 | if (childFile.isDirectory()) { 81 | if (childPackage) { 82 | myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage)); 83 | } 84 | } else { 85 | String childFilePath = childFile.getPath(); 86 | if (childFilePath.endsWith(".class")) { 87 | childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf(".")); 88 | childFilePath = childFilePath.replace("\\", "."); 89 | myClassName.add(childFilePath); 90 | } 91 | } 92 | } 93 | 94 | return myClassName; 95 | } 96 | 97 | /** 98 | * 从jar获取某包下所有类 99 | * 100 | * @param jarPath jar文件路径 101 | * @param childPackage 是否遍历子包 102 | * 103 | * @return 类的完整名称 104 | */ 105 | private static List getClassNameByJar(String jarPath, boolean childPackage) { 106 | List myClassName = new ArrayList<>(); 107 | String[] jarInfo = jarPath.split("!"); 108 | String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/")); 109 | String packagePath = jarInfo[1].substring(1); 110 | try (JarFile jarFile = new JarFile(URLDecoder.decode(jarFilePath))) { 111 | Enumeration entrys = jarFile.entries(); 112 | while (entrys.hasMoreElements()) { 113 | JarEntry jarEntry = entrys.nextElement(); 114 | String entryName = jarEntry.getName(); 115 | if (entryName.endsWith(".class")) { 116 | if (childPackage) { 117 | if (entryName.startsWith(packagePath)) { 118 | entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf(".")); 119 | myClassName.add(entryName); 120 | } 121 | } else { 122 | int index = entryName.lastIndexOf("/"); 123 | String myPackagePath; 124 | if (index != -1) { 125 | myPackagePath = entryName.substring(0, index); 126 | } else { 127 | myPackagePath = entryName; 128 | } 129 | if (myPackagePath.equals(packagePath)) { 130 | entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf(".")); 131 | myClassName.add(entryName); 132 | } 133 | } 134 | } 135 | } 136 | } catch (Exception e) { 137 | e.printStackTrace(); 138 | } 139 | return myClassName; 140 | } 141 | 142 | /** 143 | * 从所有jar中搜索该包,并获取该包下所有类 144 | * 145 | * @param urls URL集合 146 | * @param packagePath 包路径 147 | * @param childPackage 是否遍历子包 148 | * 149 | * @return 类的完整名称 150 | */ 151 | private static List getClassNameByJars(URL[] urls, String packagePath, boolean childPackage) { 152 | List myClassName = new ArrayList<>(); 153 | if (urls != null) { 154 | for (URL url : urls) { 155 | String urlPath = url.getPath(); 156 | // 不必搜索classes文件夹 157 | if (urlPath.endsWith("classes/")) { 158 | continue; 159 | } 160 | String jarPath = urlPath + "!/" + packagePath; 161 | myClassName.addAll(getClassNameByJar(jarPath, childPackage)); 162 | } 163 | } 164 | return myClassName; 165 | } 166 | 167 | /** 168 | * 加载类 169 | * 170 | * @param c 类 171 | */ 172 | public static void load(Class c) { 173 | if (c != null) load(c.getName()); 174 | } 175 | 176 | /** 177 | * 加载类 178 | * 179 | * @param c 类 180 | */ 181 | public static void load(String c) { 182 | try { 183 | Class.forName(c); 184 | } catch (Throwable e) { 185 | e.printStackTrace(); 186 | } 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/Cmd.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月10日 4 | * file: Cmds.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bukkit.cmds; 9 | 10 | import lombok.NonNull; 11 | import lombok.val; 12 | import org.bukkit.command.Command; 13 | import org.bukkit.command.CommandSender; 14 | import org.bukkit.entity.Player; 15 | import yuan.plugins.serverDo.Tool; 16 | import yuan.plugins.serverDo.bukkit.Core; 17 | import yuan.plugins.serverDo.bukkit.Core.CallbackQueue; 18 | import yuan.plugins.serverDo.bukkit.MESSAGE; 19 | import yuan.plugins.serverDo.bukkit.Main; 20 | import yuan.plugins.serverDo.bukkit.cmds.CommandManager.CmdInfo; 21 | 22 | import java.util.ArrayList; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | /** 29 | * Cmd 30 | * 31 | * @author yuanlu 32 | */ 33 | public abstract class Cmd extends Command implements MESSAGE { 34 | /** 执行计数 */ 35 | public static final HashMap, AtomicInteger> EXECUTE_COUNT = new HashMap<>(); 36 | /** 所有的信息 */ 37 | private static final HashMap MSGS = new HashMap<>(); 38 | /** 此命令名称 */ 39 | protected final String cmdName; 40 | 41 | /** 42 | * 构造一个命令 43 | * 44 | * @param name 名称 45 | */ 46 | Cmd(String name) { 47 | super(name); 48 | val info = getCmdInfo(); 49 | setDescription(info.getDescription()); 50 | setUsage(info.getUsageMessage()); 51 | setPermission(info.getPermission()); 52 | 53 | cmdName = getCmdName(getClass()); 54 | } 55 | 56 | /** 57 | * bstats统计: 执行命令 58 | * 59 | * @param cmd 命令 60 | */ 61 | protected static void bstatsExecute(Cmd cmd) { 62 | EXECUTE_COUNT.computeIfAbsent(cmd.getClass(), x -> new AtomicInteger()).getAndIncrement(); 63 | } 64 | 65 | /** 66 | * 从list中挑选合适的文字返回
67 | * 不会修改原列表 68 | * 69 | * @param prefix 前缀 70 | * @param nullList 当前缀内容为空时返回的列表(做默认值用) 71 | * @param dataList 数据列表 72 | * 73 | * @return 新列表 74 | */ 75 | protected static List get(String prefix, List nullList, List dataList) { 76 | if (prefix == null || prefix.isEmpty()) { 77 | ArrayList list = new ArrayList<>(dataList.size() + nullList.size()); 78 | list.addAll(nullList); 79 | list.addAll(dataList); 80 | return list; 81 | } 82 | ArrayList list = new ArrayList<>(dataList.size()); 83 | dataList.forEach(x -> { 84 | if (x != null && x.startsWith(prefix)) list.add(x); 85 | }); 86 | return list; 87 | } 88 | 89 | /** 90 | * 获取cmd名称 91 | * 92 | * @param cmd 命令 93 | * 94 | * @return 命令名称 95 | */ 96 | public static String getCmdName(Class cmd) { 97 | String cname = cmd.getSimpleName(); 98 | if (cname.length() >= 3 && cname.substring(0, 3).equalsIgnoreCase("cmd")) cname = cname.substring(3); 99 | cname = Tool.humpTrans(cname, "-"); 100 | return cname; 101 | } 102 | 103 | /** 104 | * 返回此命令的某个消息 105 | * 106 | * @param cmd 命令 107 | * @param type 消息类型 108 | * 109 | * @return 消息 110 | */ 111 | protected static Msg msg(Class cmd, String type) { 112 | val key = getCmdName(cmd) + "." + type; 113 | Msg msg = MSGS.get(key); 114 | if (msg != null) return msg; 115 | MSGS.put(key, msg = Main.getMain().mes("cmd." + key)); 116 | return msg; 117 | } 118 | 119 | /** 120 | * 检查CommandSender状态 121 | * 122 | * @param sender CommandSender 123 | * @param next 回调 124 | */ 125 | protected void checkPlayer(CommandSender sender, Runnable next) { 126 | if (sender instanceof Player) next.run(); 127 | else NOT_PLAYER.send(sender); 128 | } 129 | 130 | @Override 131 | public boolean execute(@NonNull CommandSender sender, @NonNull String commandLabel, String @NonNull [] args) { 132 | bstatsExecute(this); 133 | val cq = new CallbackQueue(); 134 | cq.task(() -> checkPlayer(sender, cq), // 135 | () -> Core.permissionCheck(sender, getPermission(), cq), // 136 | () -> execute0(sender, args)); 137 | return true; 138 | } 139 | 140 | /** 141 | * 实际执行 142 | * 143 | * @param sender Source object which is executing this command 144 | * @param args All arguments passed to the command, split via ' ' 145 | * 146 | * @return true if the command was successful, otherwise false 147 | */ 148 | protected abstract boolean execute0(CommandSender sender, String[] args); 149 | 150 | /** @return Info */ 151 | protected CmdInfo getCmdInfo() { 152 | return CommandManager.INFOS.get(getName()); 153 | } 154 | 155 | /** 156 | * 返回此命令的某个消息 157 | * 158 | * @param type 消息类型 159 | * 160 | * @return 消息 161 | */ 162 | protected final Msg msg(String type) { 163 | return msg(type, 0); 164 | } 165 | 166 | /** 167 | * 返回此命令的某个消息 168 | * 169 | * @param type 消息类型 170 | * @param sender 目标 171 | * 172 | * @return true 173 | */ 174 | protected final boolean msg(String type, CommandSender sender) { 175 | msg(type).send(sender); 176 | return true; 177 | } 178 | 179 | /** 180 | * 返回此命令的某个消息 181 | * 182 | * @param type 消息类型 183 | * @param sender 目标 184 | * @param args 参数 185 | * 186 | * @return true 187 | */ 188 | protected final boolean msg(String type, CommandSender sender, Map args) { 189 | msg(type).send(sender, args); 190 | return true; 191 | } 192 | 193 | /** 194 | * 返回此命令的某个消息 195 | * 196 | * @param type 消息类型 197 | * @param sender 目标 198 | * @param args 参数 199 | * 200 | * @return true 201 | */ 202 | protected final boolean msg(String type, CommandSender sender, Object... args) { 203 | msg(type).send(sender, args); 204 | return true; 205 | } 206 | 207 | /** 208 | * 返回此命令的某个消息 209 | * 210 | * @param type 消息类型 211 | * @param code 消息格式 212 | * 213 | * @return 消息 214 | */ 215 | protected final Msg msg(String type, int code) { 216 | val key = cmdName + "." + type; 217 | Msg msg = MSGS.get(key + code); 218 | if (msg != null) return msg; 219 | MSGS.put(key + code, msg = Main.getMain().mes("cmd." + key, code)); 220 | return msg; 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdBack.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo.bukkit.cmds; 2 | 3 | import lombok.val; 4 | import org.bukkit.command.CommandSender; 5 | import org.bukkit.entity.Player; 6 | import yuan.plugins.serverDo.bukkit.Core; 7 | 8 | /** 9 | * back命令 10 | * 11 | * @author yuanlu 12 | */ 13 | public final class CmdBack extends Cmd { 14 | 15 | /** @param name 命令名 */ 16 | CmdBack(String name) { 17 | super(name); 18 | } 19 | 20 | @Override 21 | protected boolean execute0(CommandSender sender, String[] args) { 22 | val player = (Player) sender; 23 | val loc = Core.BackHandler.getBack(player); 24 | if (loc == null) return msg("non-back", sender); 25 | msg("backing", sender); 26 | Core.tpTo(player, loc); 27 | 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdDelHome.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.bukkit.Core; 12 | import yuan.plugins.serverDo.bukkit.Main; 13 | 14 | /** 15 | * delhome命令 16 | * 17 | * @author yuanlu 18 | */ 19 | public final class CmdDelHome extends TabHome { 20 | 21 | /** @param name 命令名 */ 22 | CmdDelHome(String name) { 23 | super(name); 24 | } 25 | 26 | @Override 27 | protected boolean execute0(CommandSender sender, String[] args) { 28 | if (args.length > 0) { 29 | val player = (Player) sender; 30 | val name = args[0]; 31 | Core.listenCallBack(player, Channel.HOME, 1, (BoolConsumer) success -> { 32 | msg(success ? "success" : "fail", player, name); 33 | }); 34 | Main.send(player, Channel.Home.s1C_delHome(name)); 35 | } else msg("help", sender); 36 | return true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdDelSpawn.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.bukkit.Core; 12 | import yuan.plugins.serverDo.bukkit.Main; 13 | 14 | /** 15 | * delspawn命令 16 | * 17 | * @author yuanlu 18 | */ 19 | public final class CmdDelSpawn extends Cmd { 20 | 21 | /** @param name 命令名 */ 22 | CmdDelSpawn(String name) { 23 | super(name); 24 | } 25 | 26 | @Override 27 | protected boolean execute0(CommandSender sender, String[] args) { 28 | val player = (Player) sender; 29 | Core.listenCallBack(player, Channel.WARP, 1, (BoolConsumer) success -> { 30 | msg(success ? "success" : "fail", player, CmdSpawn.NAME); 31 | }); 32 | Main.send(player, Channel.Warp.s1C_delWarp(CmdSpawn.NAME)); 33 | return true; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdDelWarp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.bukkit.Core; 12 | import yuan.plugins.serverDo.bukkit.Main; 13 | 14 | /** 15 | * delwarp命令 16 | * 17 | * @author yuanlu 18 | */ 19 | public final class CmdDelWarp extends TabWarp { 20 | 21 | /** @param name 命令名 */ 22 | CmdDelWarp(String name) { 23 | super(name); 24 | } 25 | 26 | @Override 27 | protected boolean execute0(CommandSender sender, String[] args) { 28 | if (args.length > 0) { 29 | val player = (Player) sender; 30 | val name = args[0]; 31 | Core.listenCallBack(player, Channel.WARP, 1, (BoolConsumer) success -> { 32 | msg(success ? "success" : "fail", player, name); 33 | }); 34 | Main.send(player, Channel.Warp.s1C_delWarp(name)); 35 | } else msg("help", sender); 36 | return true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdHome.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.Tool; 12 | import yuan.plugins.serverDo.bukkit.Core; 13 | import yuan.plugins.serverDo.bukkit.Main; 14 | 15 | import java.util.Collection; 16 | import java.util.function.BiConsumer; 17 | 18 | /** 19 | * home命令 20 | * 21 | * @author yuanlu 22 | */ 23 | public final class CmdHome extends TabHome { 24 | 25 | /** @param name 命令名 */ 26 | CmdHome(String name) { 27 | super(name); 28 | } 29 | 30 | @Override 31 | protected boolean execute0(CommandSender sender, String[] args) { 32 | val player = (Player) sender; 33 | if (args.length > 0) { 34 | val arg = args[0]; 35 | Core.listenCallBack(player, Channel.HOME, 2, (BiConsumer) (name, server) -> { 36 | if (name.isEmpty()) { 37 | msg("not-found", sender, arg); 38 | } else { 39 | msg("tp", sender, name, server); 40 | Core.listenCallBack(player, Channel.HOME, 3, (BoolConsumer) success -> { 41 | if (!success) BC_ERROR.send(sender); 42 | }); 43 | Main.send(player, Channel.Home.s3C_tpHome(name)); 44 | } 45 | }); 46 | Main.send(player, Channel.Home.s2C_searchHome(arg)); 47 | } else { 48 | Core.listenCallBack(player, Channel.HOME, 4, (BiConsumer, Collection>) (w1, w2) -> { 49 | val s1 = Tool.join(w1, msg("list-w1", 1).getMsg(), msg("list-element", 1).getMsg(), msg("list-delimiter", 1).getMsg()); 50 | val s2 = Tool.join(w2, msg("list-w2", 1).getMsg(), msg("list-element", 1).getMsg(), msg("list-delimiter", 1).getMsg()); 51 | msg("list", player, s1, s2); 52 | }); 53 | Main.send(player, Channel.Home.s4C_listHome()); 54 | } 55 | return true; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdReload.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.NonNull; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.command.ConsoleCommandSender; 9 | import yuan.plugins.serverDo.bukkit.Main; 10 | 11 | /** 12 | * reload命令 13 | * 14 | * @author yuanlu 15 | */ 16 | public final class CmdReload extends Cmd { 17 | 18 | /** @param name 命令名 */ 19 | CmdReload(String name) { 20 | super(name); 21 | } 22 | 23 | @Override 24 | public boolean execute(@NonNull CommandSender sender, @NonNull String commandLabel, String @NonNull [] args) { 25 | if (sender instanceof ConsoleCommandSender) { 26 | Main.getMain().reload(); 27 | msg("success", sender); 28 | } else { 29 | msg("only-console", sender); 30 | } 31 | return true; 32 | } 33 | 34 | @Override 35 | protected boolean execute0(CommandSender sender, String[] args) { 36 | throw new InternalError(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdSetHome.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.bukkit.Core; 12 | import yuan.plugins.serverDo.bukkit.Core.Permissions; 13 | import yuan.plugins.serverDo.bukkit.Main; 14 | 15 | /** 16 | * sethome命令 17 | * 18 | * @author yuanlu 19 | */ 20 | public final class CmdSetHome extends Cmd { 21 | 22 | /** @param name 命令名 */ 23 | CmdSetHome(String name) { 24 | super(name); 25 | } 26 | 27 | @Override 28 | protected boolean execute0(CommandSender sender, String[] args) { 29 | val player = (Player) sender; 30 | val name = args.length > 0 ? args[0] : "home"; 31 | val loc = Core.toSLoc(player.getLocation()); 32 | val a = Permissions.getMaxAmount(sender, "home"); 33 | Core.listenCallBack(player, Channel.HOME, 0, (BoolConsumer) success -> { 34 | msg(success ? "success" : "fail", player, name, a); 35 | }); 36 | Main.send(player, Channel.Home.s0C_setHome(name, loc, a)); 37 | return true; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdSetSpawn.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.bukkit.Core; 11 | import yuan.plugins.serverDo.bukkit.Main; 12 | 13 | /** 14 | * setwarp命令 15 | * 16 | * @author yuanlu 17 | */ 18 | public final class CmdSetSpawn extends Cmd { 19 | 20 | /** @param name 命令名 */ 21 | CmdSetSpawn(String name) { 22 | super(name); 23 | } 24 | 25 | @Override 26 | protected boolean execute0(CommandSender sender, String[] args) { 27 | val player = (Player) sender; 28 | val loc = Core.toSLoc(player.getLocation()); 29 | Core.listenCallBack(player, Channel.WARP, 0, (Runnable) () -> { 30 | msg("success", player, CmdSpawn.NAME); 31 | }); 32 | Main.send(player, Channel.Warp.s0C_setWarp(CmdSpawn.NAME, loc)); 33 | return true; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdSetWarp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.bukkit.Core; 11 | import yuan.plugins.serverDo.bukkit.Main; 12 | 13 | /** 14 | * setwarp命令 15 | * 16 | * @author yuanlu 17 | */ 18 | public final class CmdSetWarp extends Cmd { 19 | 20 | /** @param name 命令名 */ 21 | CmdSetWarp(String name) { 22 | super(name); 23 | } 24 | 25 | @Override 26 | protected boolean execute0(CommandSender sender, String[] args) { 27 | if (args.length > 0) { 28 | val player = (Player) sender; 29 | val name = args[0]; 30 | val loc = Core.toSLoc(player.getLocation()); 31 | Core.listenCallBack(player, Channel.WARP, 0, (Runnable) () -> { 32 | msg("success", player, name); 33 | }); 34 | Main.send(player, Channel.Warp.s0C_setWarp(name, loc)); 35 | } else msg("help", sender); 36 | return true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdSpawn.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.bukkit.Core; 12 | import yuan.plugins.serverDo.bukkit.Main; 13 | 14 | import java.util.function.BiConsumer; 15 | 16 | /** 17 | * warp命令 18 | * 19 | * @author yuanlu 20 | */ 21 | public final class CmdSpawn extends Cmd { 22 | 23 | /** 24 | * spawn的warp名称 25 | */ 26 | public static final String NAME = "spawn"; 27 | 28 | /** @param name 命令名 */ 29 | CmdSpawn(String name) { 30 | super(name); 31 | } 32 | 33 | @Override 34 | protected boolean execute0(CommandSender sender, String[] args) { 35 | val player = (Player) sender; 36 | Core.listenCallBack(player, Channel.WARP, 2, (BiConsumer) (name, server) -> { 37 | if (name.isEmpty()) { 38 | msg("not-found", sender, NAME); 39 | } else { 40 | msg("tp", sender, name, server); 41 | Core.listenCallBack(player, Channel.WARP, 3, (BoolConsumer) success -> { 42 | if (!success) BC_ERROR.send(sender); 43 | }); 44 | Main.send(player, Channel.Warp.s3C_tpWarp(name)); 45 | } 46 | }); 47 | Main.send(player, Channel.Warp.s2C_searchWarp(NAME)); 48 | 49 | return true; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月10日 4 | * file: CmdTp.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bukkit.cmds; 9 | 10 | import lombok.val; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.command.CommandSender; 13 | import org.bukkit.entity.Player; 14 | import yuan.plugins.serverDo.Channel; 15 | import yuan.plugins.serverDo.Channel.Package.BiPlayerConsumer; 16 | import yuan.plugins.serverDo.ShareData; 17 | import yuan.plugins.serverDo.bukkit.Core; 18 | import yuan.plugins.serverDo.bukkit.Core.Permissions; 19 | import yuan.plugins.serverDo.bukkit.Main; 20 | 21 | import java.util.StringJoiner; 22 | import java.util.function.BiConsumer; 23 | 24 | /** 25 | * tp命令 26 | * 27 | * @author yuanlu 28 | */ 29 | public final class CmdTp extends TabTp { 30 | /** 默认命令 */ 31 | private final String defaultCmd; 32 | 33 | /** @param name 命令名 */ 34 | protected CmdTp(String name) { 35 | super(name); 36 | val def = getCmdInfo().getExtra().getString("def-cmd", "minecraft:tp"); 37 | defaultCmd = (def.startsWith("/") ? def.substring(1) : def) + " "; 38 | } 39 | 40 | @Override 41 | protected boolean execute0(CommandSender sender, String[] args) { 42 | Player player = (Player) sender; 43 | switch (args.length) { 44 | case 0: 45 | return msg("help", player); 46 | case 1:// tp target 47 | Core.listenCallBack(player, Channel.TP, 1, (BiConsumer) (name, display) -> { 48 | if (name.isEmpty()) msg("not-found", player, args[0]); 49 | else { 50 | msg("sender", player, name, display); 51 | Core.tpTo(player, name, -1, false); 52 | } 53 | }); 54 | Main.send(player, Channel.Tp.s0C_tpReq(args[0], Core.tpReqCode(player, 0))); 55 | return true; 56 | case 2:// tp mover target 57 | if (!Permissions.tpOther_(sender)) return true; 58 | Core.listenCallBack(player, Channel.TP, 0xa, (BiPlayerConsumer) (mn, md, tn, td) -> { 59 | if (mn.isEmpty()) msg("not-found", player, args[0]); 60 | else if (tn.isEmpty()) msg("not-found", player, args[1]); 61 | else { 62 | msg("third-tp", player, mn, md, tn, td); 63 | Core.tpTo(player, mn, tn, -1); 64 | } 65 | }); 66 | int code = Permissions.tpSenior(sender) ? 1 : 0;// 是否拥有高级传送权限 67 | Main.send(player, Channel.Tp.s9C_tpReqThird(args[0], args[1], code)); 68 | } 69 | StringJoiner sj = new StringJoiner(" ", defaultCmd, ""); 70 | for (val arg : args) sj.add(arg); 71 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[cmd] 转发: " + sj); 72 | Bukkit.dispatchCommand(sender, sj.toString()); 73 | return false; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTpa.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月10日 4 | * file: CmdTpa.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bukkit.cmds; 9 | 10 | import lombok.NonNull; 11 | import lombok.Value; 12 | import lombok.val; 13 | import org.bukkit.command.CommandSender; 14 | import org.bukkit.entity.Player; 15 | import yuan.plugins.serverDo.Channel; 16 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 17 | import yuan.plugins.serverDo.Tool; 18 | import yuan.plugins.serverDo.WaitMaintain; 19 | import yuan.plugins.serverDo.bukkit.Core; 20 | import yuan.plugins.serverDo.bukkit.Main; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.UUID; 26 | import java.util.concurrent.ConcurrentHashMap; 27 | import java.util.function.BiConsumer; 28 | 29 | /** 30 | * tpa命令 31 | * 32 | * @author yuanlu 33 | */ 34 | public final class CmdTpa extends TabTp { 35 | /** 传送等待 */ 36 | private static final Map> TP_WAIT = new ConcurrentHashMap<>(); 37 | 38 | /** @param name 命令名 */ 39 | protected CmdTpa(String name) { 40 | super(name); 41 | } 42 | 43 | /** 44 | * 增加传送请求(本地) 45 | * 46 | * @param player 发起玩家 47 | * @param target 指向者 48 | * @param display 指向者展示名 49 | */ 50 | static void addTpReq(@NonNull Player player, @NonNull String target, String display) { 51 | WaitMaintain.add(TP_WAIT, player.getUniqueId(), new TpWaitInfo(target, display), WaitMaintain.T_User, ArrayList::new, null); 52 | } 53 | 54 | /** 55 | * 获取玩家请求列表 56 | * 57 | * @param sender 目标 58 | * 59 | * @return 对此目标请求的玩家列表 60 | */ 61 | static List getReqList(CommandSender sender) { 62 | if (sender instanceof Player) { 63 | Player player = (Player) sender; 64 | val list = TP_WAIT.get(player.getUniqueId()); 65 | if (list != null && !list.isEmpty()) return Tool.translate(list, i -> i.sender); 66 | } 67 | return null; 68 | } 69 | 70 | /** 71 | * 移除请求 72 | * 73 | * @param player 发起玩家 74 | * @param target 指向者 75 | * @param nameAndDisplay 回调函数 76 | */ 77 | static void removeTpReq(@NonNull Player player, String target, @NonNull BiConsumer nameAndDisplay) { 78 | val list = TP_WAIT.get(player.getUniqueId()); 79 | if (list == null || list.isEmpty()) nameAndDisplay.accept(null, null); 80 | else { 81 | final TpWaitInfo tar; 82 | if (target != null) list.remove(tar = Tool.search(target, list.iterator())); 83 | else tar = list.remove(list.size() - 1); 84 | if (tar == null) nameAndDisplay.accept("", ""); 85 | else nameAndDisplay.accept(tar.getSender(), tar.getDisplay()); 86 | } 87 | } 88 | 89 | @Override 90 | protected boolean execute0(CommandSender sender, String[] args) { 91 | Player player = (Player) sender; 92 | if (args.length > 0) { 93 | val wait = Core.getWaitTime(player); 94 | if (wait < 0) { 95 | msg("cooldown", player, -wait / 1000.0); 96 | return true; 97 | } 98 | Core.listenCallBack(player, Channel.TP, 1, (BiConsumer) (name, display) -> { 99 | if (name.isEmpty()) msg("not-found", player, args[0]); 100 | else if (name.equals(player.getName())) { 101 | msg("self-tp", player); 102 | } else { 103 | msg("sender", player, name, display); 104 | addTpReq(player, name, display); 105 | Core.listenCallBack(player, Channel.TP, "5-" + name, false, WaitMaintain.T_User, (BoolConsumer) allow -> { 106 | if (allow) { 107 | Core.tpTo(player, name, wait, true); 108 | msg(wait > 0 ? "accept-wait" : "accept", player, name, display, wait); 109 | } else { 110 | msg("deny", player, name, display); 111 | } 112 | }); 113 | } 114 | }); 115 | Main.send(player, Channel.Tp.s0C_tpReq(args[0], Core.tpReqCode(player, 1))); 116 | return true; 117 | } else return msg("help", player); 118 | } 119 | 120 | /** 121 | * 传送请求等待信息 122 | * 123 | * @author yuanlu 124 | */ 125 | @Value 126 | private static final class TpWaitInfo { 127 | /** 发起请求的玩家全名 */ 128 | @NonNull 129 | String sender; 130 | /** 发起请求的玩家展示名 */ 131 | @NonNull 132 | String display; 133 | 134 | @Override 135 | public String toString() { 136 | return sender; 137 | } 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTpaccept.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月11日 4 | * file: CmdTpaccept.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bukkit.cmds; 9 | 10 | import lombok.AllArgsConstructor; 11 | import lombok.NonNull; 12 | import lombok.Value; 13 | import lombok.val; 14 | import org.bukkit.command.CommandSender; 15 | import org.bukkit.entity.Player; 16 | import yuan.plugins.serverDo.Channel; 17 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 18 | import yuan.plugins.serverDo.Tool; 19 | import yuan.plugins.serverDo.WaitMaintain; 20 | import yuan.plugins.serverDo.bukkit.Core; 21 | import yuan.plugins.serverDo.bukkit.Main; 22 | 23 | import java.util.*; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | 26 | /** 27 | * tpaccept命令 28 | * 29 | * @author yuanlu 30 | */ 31 | public final class CmdTpaccept extends Cmd { 32 | /** 传送等待 */ 33 | private static final Map> TP_WAIT = new ConcurrentHashMap<>(); 34 | /** 传送等待超时的玩家 */ 35 | private static final HashMap TP_WAIT_TIMEOUT = new HashMap<>(); 36 | /** message */ 37 | private static final Msg M_R_F = msg(CmdTpcancel.class, "remote-fail"); 38 | /** message */ 39 | private static final Msg M_R_S = msg(CmdTpcancel.class, "remote-success"); 40 | 41 | /** @param name 命令名 */ 42 | protected CmdTpaccept(String name) { 43 | super(name); 44 | } 45 | 46 | /** 47 | * 增加传送请求 48 | * 49 | * @param player 指向玩家 50 | * @param sender 发起者 51 | * @param display 发起者展示名 52 | * @param isThere 是否传送到对方位置 53 | */ 54 | public static void addTpReq(@NonNull Player player, @NonNull String sender, String display, boolean isThere) { 55 | val uid = player.getUniqueId(); 56 | TP_WAIT_TIMEOUT.remove(uid); 57 | WaitMaintain.add(TP_WAIT, uid, new TpWaitInfo(sender, display, isThere), WaitMaintain.T_User, ArrayList::new, () -> { 58 | if (player.isOnline()) TP_WAIT_TIMEOUT.put(uid, System.currentTimeMillis()); 59 | }); 60 | } 61 | 62 | /** 63 | * 取消传送请求 64 | * 65 | * @param player 指向玩家 66 | * @param sender 发起者 67 | * 68 | * @return 是否成功取消 69 | */ 70 | public static boolean cancelReq(@NonNull Player player, @NonNull String sender) { 71 | val list = TP_WAIT.get(player.getUniqueId()); 72 | if (list != null) { 73 | for (Iterator itr = list.iterator(); itr.hasNext(); ) { 74 | TpWaitInfo info = itr.next(); 75 | if (sender.equals(info.getSender())) { 76 | M_R_S.send(player, sender, info.display); 77 | itr.remove(); 78 | return true; 79 | } 80 | } 81 | } 82 | M_R_F.send(player, sender); 83 | return false; 84 | } 85 | 86 | /** 87 | * 获取玩家请求列表 88 | * 89 | * @param sender 目标 90 | * 91 | * @return 对此目标请求的玩家列表 92 | */ 93 | static List getReqList(CommandSender sender) { 94 | if (sender instanceof Player) { 95 | Player player = (Player) sender; 96 | val list = TP_WAIT.get(player.getUniqueId()); 97 | if (list != null && !list.isEmpty()) return Tool.translate(list, i -> i.sender); 98 | } 99 | return null; 100 | } 101 | 102 | /** 103 | * 处理请求 104 | * 105 | * @param player 指向玩家 106 | * @param who 请求发起者 107 | * @param accept 是否是接受请求 108 | * @param cmd 执行命令 109 | */ 110 | static void handleRequest(@NonNull Player player, String who, boolean accept, @NonNull Cmd cmd) { 111 | val list = TP_WAIT.get(player.getUniqueId()); 112 | if (list != null && !list.isEmpty()) { 113 | final TpWaitInfo tar; 114 | if (who != null) list.remove(tar = Tool.search(who, list.iterator())); 115 | else tar = list.remove(list.size() - 1); 116 | 117 | if (tar == null) cmd.msg("not-found", player, who); 118 | else { 119 | val wait = Core.getWaitTime(player); 120 | if (tar.isThere() && accept && wait < 0) { 121 | msg(CmdTpahere.class, "cooldown").send(player, -wait / 1000.0); 122 | return; 123 | } 124 | Core.listenCallBack(player, Channel.TP, 4, (BoolConsumer) success -> { 125 | if (success) { 126 | cmd.msg(accept && tar.isThere() && wait > 0 ? "success-wait" : "success", player, tar.getSender(), tar.getDisplay(), wait); 127 | if (accept && tar.isThere()) Core.tpTo(player, tar.getSender(), wait, true); 128 | } else cmd.msg("offline", player); 129 | }); 130 | Main.send(player, Channel.Tp.s3C_tpResp(tar.getSender(), accept)); 131 | } 132 | } else { 133 | val timeout = TP_WAIT_TIMEOUT.remove(player.getUniqueId()); 134 | if (timeout != null) cmd.msg("timeout", player, (System.currentTimeMillis() - timeout) / 1000.0); 135 | else cmd.msg("no-request", player); 136 | } 137 | } 138 | 139 | @Override 140 | protected boolean execute0(CommandSender sender, String[] args) { 141 | handleRequest((Player) sender, args.length > 0 ? args[0] : null, true, this); 142 | return true; 143 | } 144 | 145 | @Override 146 | public List tabComplete(CommandSender sender, String alias, String[] args) { 147 | return getReqList(sender); 148 | } 149 | 150 | /** 151 | * 传送请求等待信息 152 | * 153 | * @author yuanlu 154 | */ 155 | @Value 156 | @AllArgsConstructor 157 | private static final class TpWaitInfo { 158 | /** 发起请求的玩家全名 */ 159 | @NonNull 160 | String sender; 161 | /** 发起请求的玩家展示名 */ 162 | @NonNull 163 | String display; 164 | /** 是否是需要传送到对方位置 */ 165 | boolean isThere; 166 | 167 | @Override 168 | public int hashCode() { 169 | return sender.hashCode(); 170 | } 171 | 172 | @Override 173 | public boolean equals(Object obj) { 174 | if (obj instanceof TpWaitInfo) return sender.equals(((TpWaitInfo) obj).sender); 175 | return sender.equals(obj); 176 | } 177 | 178 | @Override 179 | public String toString() { 180 | return sender; 181 | } 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTpahere.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月10日 4 | * file: CmdTpahere.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bukkit.cmds; 9 | 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | import yuan.plugins.serverDo.Channel; 13 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 14 | import yuan.plugins.serverDo.WaitMaintain; 15 | import yuan.plugins.serverDo.bukkit.Core; 16 | import yuan.plugins.serverDo.bukkit.Main; 17 | 18 | import java.util.function.BiConsumer; 19 | 20 | /** 21 | * tpahere命令 22 | * 23 | * @author yuanlu 24 | */ 25 | public final class CmdTpahere extends TabTp { 26 | /** @param name 命令名 */ 27 | protected CmdTpahere(String name) { 28 | super(name); 29 | } 30 | 31 | @Override 32 | protected boolean execute0(CommandSender sender, String[] args) { 33 | Player player = (Player) sender; 34 | if (args.length > 0) { 35 | Core.listenCallBack(player, Channel.TP, 1, (BiConsumer) (name, display) -> { 36 | if (name.isEmpty()) msg("not-found", player, args[0]); 37 | else if (name.equals(player.getName())) { 38 | msg("self-tp", player); 39 | } else { 40 | msg("sender", player, name, display); 41 | CmdTpa.addTpReq(player, name, display); 42 | Core.listenCallBack(player, Channel.TP, "5-" + name, false, WaitMaintain.T_User, (BoolConsumer) allow -> { 43 | msg(allow ? "accept" : "deny", player, name, display); 44 | }); 45 | } 46 | }); 47 | Main.send(player, Channel.Tp.s0C_tpReq(args[0], Core.tpReqCode(player, 3))); 48 | } else return msg("help", player); 49 | return false; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTpcancel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.bukkit.Core; 11 | import yuan.plugins.serverDo.bukkit.Main; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * tpcancel命令 17 | * 18 | * @author yuanlu 19 | */ 20 | public final class CmdTpcancel extends Cmd { 21 | /** @param name 命令名 */ 22 | protected CmdTpcancel(String name) { 23 | super(name); 24 | } 25 | 26 | @Override 27 | protected boolean execute0(CommandSender sender, String[] args) { 28 | val player = (Player) sender; 29 | val who = args.length > 0 ? args[0] : null; 30 | CmdTpa.removeTpReq(player, who, (name, display) -> { 31 | if (name == null) msg("no-request", player); 32 | else if (name.isEmpty()) msg("not-found", player, who); 33 | else { 34 | Core.removeCallBack(player, Channel.TP, "5-" + name); 35 | Main.send(player, Channel.Tp.sbC_cancel(name)); 36 | msg("success", player, name, display); 37 | } 38 | }); 39 | return false; 40 | } 41 | 42 | @Override 43 | public List tabComplete(CommandSender sender, String alias, String[] args) { 44 | return CmdTpa.getReqList(sender); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTpdeny.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月11日 4 | * file: Tpdeny.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bukkit.cmds; 9 | 10 | import lombok.NonNull; 11 | import org.bukkit.command.CommandSender; 12 | import org.bukkit.entity.Player; 13 | 14 | import java.util.List; 15 | 16 | /** 17 | * tpdeny命令 18 | * 19 | * @author yuanlu 20 | */ 21 | public final class CmdTpdeny extends Cmd { 22 | 23 | /** @param name 命令名 */ 24 | protected CmdTpdeny(String name) { 25 | super(name); 26 | } 27 | 28 | @Override 29 | protected boolean execute0(CommandSender sender, String[] args) { 30 | CmdTpaccept.handleRequest((@NonNull Player) sender, args.length > 0 ? args[0] : null, false, this); 31 | return false; 32 | } 33 | 34 | @Override 35 | public List tabComplete(CommandSender sender, String alias, String[] args) { 36 | return CmdTpaccept.getReqList(sender); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTphere.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月10日 4 | * file: CmdTphere.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bukkit.cmds; 9 | 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | import yuan.plugins.serverDo.Channel; 13 | import yuan.plugins.serverDo.bukkit.Core; 14 | import yuan.plugins.serverDo.bukkit.Main; 15 | 16 | import java.util.function.BiConsumer; 17 | 18 | /** 19 | * tphere命令 20 | * 21 | * @author yuanlu 22 | */ 23 | public final class CmdTphere extends TabTp { 24 | 25 | /** @param name 命令名 */ 26 | protected CmdTphere(String name) { 27 | super(name); 28 | } 29 | 30 | @Override 31 | protected boolean execute0(CommandSender sender, String[] args) { 32 | Player player = (Player) sender; 33 | if (args.length > 0) { 34 | Core.listenCallBack(player, Channel.TP, 1, (BiConsumer) (name, display) -> { 35 | if (name.isEmpty()) msg("not-found", player, args[0]); 36 | else { 37 | msg("sender", player, name, display); 38 | Core.tpTo(name, player, -1); 39 | } 40 | }); 41 | Main.send(player, Channel.Tp.s0C_tpReq(args[0], Core.tpReqCode(player, 2))); 42 | } else return msg("help", player); 43 | return false; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdTrans.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Tool; 10 | import yuan.plugins.serverDo.bukkit.third.Third; 11 | import yuan.plugins.serverDo.bukkit.third.Third.TransMethods; 12 | 13 | import java.util.List; 14 | 15 | /** 16 | * trans命令 17 | * 18 | * @author yuanlu 19 | */ 20 | public final class CmdTrans extends Cmd { 21 | 22 | /** @param name 命令名 */ 23 | CmdTrans(String name) { 24 | super(name); 25 | } 26 | 27 | @Override 28 | protected boolean execute0(CommandSender sender, String[] args) { 29 | val player = (Player) sender; 30 | if (args.length <= 0) return msg("help", sender); 31 | 32 | val third = Third.get(args[0]); 33 | if (third == null) return msg("not-found", sender, args[0]); 34 | if (!third.isValid()) return msg("invalid", sender, third.getName()); 35 | 36 | if (args.length <= 1) return msg("help-" + third.getName(), sender); 37 | val tm = TransMethods.getByName(args[1]); 38 | if (!third.canDo(tm)) return msg("cannot", sender, third.getName(), tm); 39 | 40 | val title = msg("title", 1).getMsg(); 41 | val subtitle = msg("sub-title", 1).getMsg(); 42 | tm.handle(third, player, (n, a) -> { 43 | player.sendTitle(String.format(title, tm.getName()), String.format(subtitle, n, Math.abs(a)), 0, 100, 0); 44 | if (a <= 0) msg("success", sender, n, -a); 45 | }); 46 | return true; 47 | } 48 | 49 | @Override 50 | public List tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException { 51 | if (args.length == 1) {// plugin 52 | return Tool.getMatchList(args[0], Third.THIRDS_VIEW); 53 | } else if (args.length == 2) {// func 54 | val third = Third.get(args[0]); 55 | if (third != null) return Tool.getMatchList(args[1], third.getCanDos(), c -> c.getName()); 56 | } 57 | return super.tabComplete(sender, alias, args); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdVanish.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.bukkit.Core; 12 | import yuan.plugins.serverDo.bukkit.Main; 13 | 14 | /** 15 | * vanish命令 16 | * 17 | * @author yuanlu 18 | */ 19 | public final class CmdVanish extends Cmd { 20 | 21 | /** @param name 命令名 */ 22 | CmdVanish(String name) { 23 | super(name); 24 | } 25 | 26 | /** 27 | * 回调函数
28 | * 主动回调 29 | * 30 | * @param player 玩家 31 | */ 32 | public static void callback(Player player) { 33 | msg(CmdVanish.class, "auto-hide").send(player); 34 | } 35 | 36 | @Override 37 | protected boolean execute0(CommandSender sender, String[] args) { 38 | val player = (Player) sender; 39 | boolean isAlway = false; 40 | if (args.length > 0) { 41 | switch (args[0].toLowerCase()) { 42 | case "a": 43 | case "alway": 44 | case "always": 45 | case "总是": 46 | isAlway = true; 47 | break; 48 | default: 49 | msg("help", sender); 50 | return true; 51 | } 52 | } 53 | val node = isAlway ? "always-" : ""; 54 | Core.listenCallBack(player, Channel.VANISH, null, (BoolConsumer) hide -> { 55 | msg(node + (hide ? "hide" : "show"), player); 56 | }); 57 | Main.send(player, Channel.Vanish.sendS(true)); 58 | return false; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CmdWarp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.val; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.Package.BoolConsumer; 11 | import yuan.plugins.serverDo.Tool; 12 | import yuan.plugins.serverDo.bukkit.Core; 13 | import yuan.plugins.serverDo.bukkit.Main; 14 | 15 | import java.util.Collection; 16 | import java.util.function.BiConsumer; 17 | 18 | /** 19 | * warp命令 20 | * 21 | * @author yuanlu 22 | */ 23 | public final class CmdWarp extends TabWarp { 24 | 25 | /** @param name 命令名 */ 26 | CmdWarp(String name) { 27 | super(name); 28 | } 29 | 30 | @Override 31 | protected boolean execute0(CommandSender sender, String[] args) { 32 | val player = (Player) sender; 33 | if (args.length > 0) { 34 | val arg = args[0]; 35 | Core.listenCallBack(player, Channel.WARP, 2, (BiConsumer) (name, server) -> { 36 | if (name.isEmpty()) { 37 | msg("not-found", sender, arg); 38 | } else { 39 | msg("tp", sender, name, server); 40 | Core.listenCallBack(player, Channel.WARP, 3, (BoolConsumer) success -> { 41 | if (!success) BC_ERROR.send(sender); 42 | }); 43 | Main.send(player, Channel.Warp.s3C_tpWarp(name)); 44 | } 45 | }); 46 | Main.send(player, Channel.Warp.s2C_searchWarp(arg)); 47 | } else { 48 | Core.listenCallBack(player, Channel.WARP, 4, (BiConsumer, Collection>) (w1, w2) -> { 49 | val s1 = Tool.join(w1, msg("list-w1", 1).getMsg(), msg("list-element", 1).getMsg(), msg("list-delimiter", 1).getMsg()); 50 | val s2 = Tool.join(w2, msg("list-w2", 1).getMsg(), msg("list-element", 1).getMsg(), msg("list-delimiter", 1).getMsg()); 51 | msg("list", player, s1, s2); 52 | }); 53 | Main.send(player, Channel.Warp.s4C_listWarp()); 54 | } 55 | return true; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/CommandManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author yuanlu 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.*; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.command.Command; 9 | import org.bukkit.command.CommandMap; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.configuration.ConfigurationSection; 12 | import yuan.plugins.serverDo.ShareData; 13 | import yuan.plugins.serverDo.bukkit.MESSAGE; 14 | import yuan.plugins.serverDo.bukkit.Main; 15 | import yuan.plugins.serverDo.bukkit.PackageUtil; 16 | 17 | import java.lang.reflect.Method; 18 | import java.lang.reflect.Modifier; 19 | import java.util.*; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * 命令管理器 24 | * 25 | * @author yuanlu 26 | */ 27 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 28 | public final class CommandManager implements MESSAGE { 29 | /** 30 | * 所有的命令
31 | * {@code 命令名->命令信息} 32 | */ 33 | static final HashMap INFOS = new HashMap<>(); 34 | static final HashMap CMD_INSTANCE = new HashMap<>(); 35 | 36 | public static List tabParse(@NonNull CommandSender player, @NonNull String cmdline) { 37 | 38 | val start = cmdline.startsWith("/") ? 1 : 0; 39 | val end = cmdline.indexOf(' '); 40 | val cmd = cmdline.substring(start, end < 0 ? cmdline.length() : end); 41 | val instance = CMD_INSTANCE.get(cmd); 42 | if (instance == null) return Collections.emptyList(); 43 | 44 | return instance.tabComplete(player, cmd, end < 0 ? new String[0] : cmdline.substring(end + 1).split(" ")); 45 | } 46 | 47 | /** 48 | * 初始化所有命令 49 | * 50 | * @param conf 配置文件 51 | */ 52 | @SuppressWarnings("unchecked") 53 | public static void init(ConfigurationSection conf) { 54 | if (conf == null) return; 55 | val names = PackageUtil.getClassName(Cmd.class.getPackage().getName(), false); 56 | for (val name : names) { 57 | try { 58 | val c = Class.forName(name); 59 | if (c == Cmd.class || !Cmd.class.isAssignableFrom(c) ||// 60 | Modifier.isAbstract(c.getModifiers()) || Modifier.isInterface(c.getModifiers())) { 61 | // ShareData.getLogger().warning("[CMD] 非法命令: " + name); 62 | continue; 63 | } 64 | init(conf, (Class) c); 65 | } catch (ClassNotFoundException e) { 66 | ShareData.getLogger().warning("[CMD] 未知命令: " + name); 67 | e.printStackTrace(); 68 | } 69 | } 70 | 71 | } 72 | 73 | /** 74 | * 初始化一条命令 75 | * 76 | * @param conf 配置文件 77 | * @param c 目标类 78 | */ 79 | private static void init(ConfigurationSection conf, Class c) { 80 | try { 81 | val cmd = Cmd.getCmdName(c); 82 | conf = conf.getConfigurationSection(cmd); 83 | if (conf == null) { 84 | ShareData.getLogger().warning("关闭命令: " + cmd); 85 | return; 86 | } 87 | val constructor = c.getDeclaredConstructor(String.class); 88 | val info = CmdInfo.getCmdInfo(conf); 89 | if (info.getNames().isEmpty()) ShareData.getLogger().warning("关闭命令: " + cmd); 90 | else for (val cmdName : info.getNames()) { 91 | INFOS.put(cmdName, info); 92 | val instance = register(constructor.newInstance(cmdName)); 93 | CMD_INSTANCE.put(cmdName, instance); 94 | } 95 | } catch (Exception e) { 96 | ShareData.getLogger().warning("注册命令时出错: " + c); 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | /** 102 | * 注册命令
103 | * 将命令注册到Bukkit上 104 | * 105 | * @param cmd 106 | * @param cmd 命令 107 | * 108 | * @return input 109 | */ 110 | public static T register(T cmd) { 111 | try { 112 | Method method = Bukkit.getServer().getClass().getMethod("getCommandMap"); 113 | CommandMap cmdm = (CommandMap) method.invoke(Bukkit.getServer()); 114 | val b = cmdm.register(Main.getMain().getName(), cmd); 115 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[D] register cmd: " + b + ", fbn:" + Main.getMain().getName() + ": " + cmd.getName()); 116 | } catch (Exception e2) { 117 | System.err.println("CAN NOT REGISTER COMMAND: " + e2); 118 | Main.getMain().getLogger().warning("CAN NOT REGISTER COMMAND: " + e2); 119 | } 120 | return cmd; 121 | } 122 | 123 | /** 获取所有命令名称 */ 124 | public static ArrayList getCommandNames() { 125 | return new ArrayList<>(INFOS.keySet()); 126 | } 127 | 128 | /** 命令信息 */ 129 | @Value 130 | static class CmdInfo { 131 | /** 描述 */ 132 | String description; 133 | /** 使用方法 */ 134 | String usageMessage; 135 | /** 所有名称 */ 136 | List names; 137 | /** 权限 */ 138 | String permission; 139 | /** 额外数据 */ 140 | ConfigurationSection extra; 141 | 142 | /** 143 | * 获取命令信息 144 | * 145 | * @param conf 配置节点 146 | * 147 | * @return 命令信息 148 | */ 149 | private static @NonNull CmdInfo getCmdInfo(@NonNull ConfigurationSection conf) { 150 | val names = conf.isString("names") ? Collections.singletonList(conf.getString("names", null)) : conf.getStringList("names"); 151 | val permission = conf.getString("permission", null); 152 | val usageMessage = conf.getString("usageMessage", ""); 153 | val description = conf.getString("description", ""); 154 | return new CmdInfo(description, usageMessage, names.stream().filter(Objects::nonNull).filter(s -> !s.isEmpty()).collect(Collectors.toList()), 155 | permission, conf); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/TabHome.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.NonNull; 7 | import lombok.val; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import yuan.plugins.serverDo.ShareData; 11 | import yuan.plugins.serverDo.ShareData.TabType; 12 | import yuan.plugins.serverDo.bukkit.Core; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * tab 19 | * 20 | * @author yuanlu 21 | */ 22 | public abstract class TabHome extends Cmd { 23 | 24 | /** @param name 命令名 */ 25 | TabHome(String name) { 26 | super(name); 27 | } 28 | 29 | @Override 30 | public @NonNull List tabComplete(@NonNull CommandSender sender, @NonNull String alias, String @NonNull [] args) { 31 | if (sender instanceof Player) { 32 | val l = Core.TabHandler.getTab((Player) sender, args.length > 0 ? args[args.length - 1] : "", TabType.HOME); 33 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[TAB] " + l); 34 | return l; 35 | } 36 | return Collections.emptyList(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/TabTp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.NonNull; 7 | import lombok.val; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import yuan.plugins.serverDo.ShareData; 11 | import yuan.plugins.serverDo.bukkit.Core; 12 | import yuan.plugins.serverDo.bukkit.Core.Permissions; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * tab 19 | * 20 | * @author yuanlu 21 | */ 22 | public abstract class TabTp extends Cmd { 23 | 24 | /** @param name 命令名 */ 25 | TabTp(String name) { 26 | super(name); 27 | } 28 | 29 | @Override 30 | public @NonNull List tabComplete(@NonNull CommandSender sender, @NonNull String alias, String @NonNull [] args) { 31 | if (sender instanceof Player) { 32 | val player = (Player) sender; 33 | val l = Core.TabHandler.getTabReplaceTp(player, args.length > 0 ? args[args.length - 1] : "", Permissions.tpSenior(player)); 34 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[TAB] " + l); 35 | return l; 36 | } 37 | return Collections.emptyList(); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/TabWarp.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.cmds; 5 | 6 | import lombok.NonNull; 7 | import lombok.val; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import yuan.plugins.serverDo.ShareData; 11 | import yuan.plugins.serverDo.ShareData.TabType; 12 | import yuan.plugins.serverDo.bukkit.Core; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * tab 19 | * 20 | * @author yuanlu 21 | */ 22 | public abstract class TabWarp extends Cmd { 23 | 24 | /** @param name 命令名 */ 25 | TabWarp(String name) { 26 | super(name); 27 | } 28 | 29 | @Override 30 | public @NonNull List tabComplete(@NonNull CommandSender sender, @NonNull String alias, String @NonNull [] args) { 31 | if (sender instanceof Player) { 32 | val l = Core.TabHandler.getTab((Player) sender, args.length > 0 ? args[args.length - 1] : "", TabType.WARP); 33 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[TAB] " + l); 34 | return l; 35 | } 36 | return Collections.emptyList(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/cmds/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月9日 4 | * file: package-info.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | *

8 | * Bukkit端命令相关 9 | * 10 | * @author yuanlu 11 | *

12 | * Bukkit端命令相关 13 | * @author yuanlu 14 | */ 15 | /** 16 | * Bukkit端命令相关 17 | * 18 | * @author yuanlu 19 | * 20 | */ 21 | package yuan.plugins.serverDo.bukkit.cmds; 22 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月9日 4 | * file: package-info.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | *

8 | * Bukkit端代码 9 | * 10 | * @author yuanlu 11 | *

12 | * Bukkit端代码 13 | * @author yuanlu 14 | */ 15 | /** 16 | * Bukkit端代码 17 | * 18 | * @author yuanlu 19 | * 20 | */ 21 | package yuan.plugins.serverDo.bukkit; 22 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/third/Third.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bukkit.third; 5 | 6 | import lombok.AllArgsConstructor; 7 | import lombok.Getter; 8 | import lombok.NonNull; 9 | import lombok.val; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.Location; 12 | import org.bukkit.entity.Player; 13 | import yuan.plugins.serverDo.Channel; 14 | import yuan.plugins.serverDo.Channel.Package.BiIntConsumer; 15 | import yuan.plugins.serverDo.ShareData; 16 | import yuan.plugins.serverDo.bukkit.Core; 17 | import yuan.plugins.serverDo.bukkit.Main; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | import java.lang.reflect.Method; 22 | import java.util.*; 23 | import java.util.function.IntConsumer; 24 | 25 | import static java.lang.annotation.ElementType.METHOD; 26 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 27 | 28 | /** 29 | * 第三方数据 30 | * 31 | * @author yuanlu 32 | */ 33 | @Getter 34 | public abstract class Third { 35 | /** 第三方数据 */ 36 | private static final HashMap THIRDS = new HashMap<>(); 37 | /** 第三方数据的视图 */ 38 | public static final @NonNull Set THIRDS_VIEW = Collections.unmodifiableSet(THIRDS.keySet()); 39 | /** 所有的转换方法(处理函数) */ 40 | private static final EnumMap TRANS_FUNCS_CALL = new EnumMap<>(TransMethods.class); 41 | /** 所有的转换方法(实现函数) */ 42 | private static final ArrayList TRANS_FUNCS = new ArrayList<>(); 43 | 44 | static { 45 | try { 46 | val ms = Third.class.getDeclaredMethods(); 47 | for (val m : ms) { 48 | val a = m.getDeclaredAnnotation(TransFunc.class); 49 | if (a != null) { 50 | TRANS_FUNCS.add(m); 51 | TRANS_FUNCS_CALL.put(a.value(), Third.class.getDeclaredMethod(m.getName(), Player.class, BiIntConsumer.class)); 52 | } 53 | } 54 | } catch (Throwable e) { 55 | e.printStackTrace(); 56 | } 57 | // registerThird(CMI.INSTANCE);TODO 58 | } 59 | 60 | /** 目标插件名 */ 61 | public final @NonNull String targetPluginName; 62 | /** 别名 */ 63 | public final @NonNull String name; 64 | /** 所有可以完成的功能 */ 65 | private final Set canDos; 66 | 67 | /** 68 | * 构造 69 | * 70 | * @param targetPluginName 目标插件名 71 | * @param name 别名 72 | */ 73 | public Third(@NonNull String targetPluginName, @NonNull String name) { 74 | this.targetPluginName = targetPluginName; 75 | this.name = name; 76 | this.canDos = Collections.unmodifiableSet(canDo(this.getClass())); 77 | } 78 | 79 | /** 80 | * 获取某个转换器可以完成的功能 81 | * 82 | * @param c 转换器的类 83 | * 84 | * @return 可以完成的功能 85 | */ 86 | private static EnumSet canDo(Class c) { 87 | EnumSet methods = EnumSet.noneOf(TransMethods.class); 88 | for (val m : TRANS_FUNCS) { 89 | try { 90 | val cm = c.getMethod(m.getName(), m.getParameterTypes()); 91 | if (cm.getDeclaringClass() != Third.class) methods.add(m.getAnnotation(TransFunc.class).value()); 92 | } catch (Throwable e) { 93 | e.printStackTrace(); 94 | } 95 | } 96 | return methods; 97 | } 98 | 99 | /** 100 | * 获取处理器 101 | * 102 | * @param third 名称 103 | * 104 | * @return 处理器/null 105 | */ 106 | public static Third get(@NonNull String third) { 107 | return THIRDS.get(third.toLowerCase()); 108 | } 109 | 110 | /** @param third 第三方插件的处理器 */ 111 | public static final void registerThird(Third third) { 112 | THIRDS.put(third.getName().toLowerCase(), third); 113 | } 114 | 115 | /** 116 | * 检测此转换器是否能够完成某个功能 117 | * 118 | * @param m 功能 119 | * 120 | * @return 是否能完成 121 | */ 122 | public boolean canDo(TransMethods m) { 123 | return canDos.contains(m); 124 | } 125 | 126 | /** 127 | * 获取所有玩家的Home 128 | * 129 | * @return 玩家UUID-家名-家坐标 130 | */ 131 | @TransFunc(TransMethods.ALL_HOME) 132 | @NonNull 133 | public HashMap> getAllHomes() { 134 | throw new UnsupportedOperationException(); 135 | } 136 | 137 | /** 138 | * @param p 玩家 139 | * @param process 过程 140 | * 141 | * @see #getAllHomes() 142 | */ 143 | public final void getAllHomes(Player p, BiIntConsumer process) { 144 | @NonNull val data = getAllHomes(); 145 | int all = 0, now = 0; 146 | for (val homes : data.values()) all += homes.size(); 147 | if (all > 0) for (val e : data.entrySet()) { 148 | val uuid = e.getKey(); 149 | val homes = e.getValue(); 150 | if (!homes.isEmpty()) for (val he : homes.entrySet()) { 151 | val name = he.getKey(); 152 | val home = he.getValue(); 153 | Main.send(p, Channel.TransHome.sendS(uuid, name, Core.toSLoc(home))); 154 | process.accept(now++, all); 155 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[TRANS] " + uuid + " " + name + " " + home); 156 | } 157 | } 158 | 159 | val ALL = -all; 160 | Core.listenCallBack(p, Channel.TRANS_HOME, null, (IntConsumer) amount -> { 161 | process.accept(amount, ALL); 162 | }); 163 | Main.send(p, Channel.TransHome.sendS()); 164 | } 165 | 166 | /** 167 | * 获取所有的Warp 168 | * 169 | * @return 地标名-地标坐标 170 | */ 171 | @TransFunc(TransMethods.ALL_WARP) 172 | @NonNull 173 | public LinkedHashMap getAllWarps() { 174 | throw new UnsupportedOperationException(); 175 | } 176 | 177 | /** 178 | * @param p 玩家 179 | * @param process 过程 180 | * 181 | * @see #getAllWarps() 182 | */ 183 | public final void getAllWarps(Player p, BiIntConsumer process) { 184 | @NonNull val data = getAllWarps(); 185 | val all = data.size(); 186 | int now = 0; 187 | for (val warp : data.entrySet()) { 188 | val name = warp.getKey(); 189 | val loc = warp.getValue(); 190 | Main.send(p, Channel.TransWarp.sendS(name, Core.toSLoc(loc))); 191 | process.accept(now++, all); 192 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[TRANS] " + name + " " + loc); 193 | } 194 | 195 | Core.listenCallBack(p, Channel.TRANS_WARP, null, (IntConsumer) amount -> { 196 | process.accept(amount, -all); 197 | }); 198 | Main.send(p, Channel.TransWarp.sendS()); 199 | } 200 | 201 | /** @return 此第三方数据是否有效 */ 202 | public boolean isValid() { 203 | return Bukkit.getPluginManager().getPlugin(targetPluginName) != null; 204 | } 205 | 206 | /** 207 | * 所有的转换方法 208 | * 209 | * @author yuanlu 210 | */ 211 | @AllArgsConstructor 212 | @Getter 213 | public enum TransMethods { 214 | /** @see Third#getAllHomes() */ 215 | ALL_HOME("all-home"), 216 | /** @see Third#getAllWarps() */ 217 | ALL_WARP("warps"); 218 | 219 | /** 集合 */ 220 | private static final HashMap VS = new HashMap<>(); 221 | 222 | static { 223 | for (val v : values()) VS.put(v.getName().toLowerCase(), v); 224 | } 225 | 226 | /** 名称 */ 227 | private final String name; 228 | 229 | /** 230 | * 通过名字获取 231 | * 232 | * @param name 名字 233 | * 234 | * @return 方法 235 | */ 236 | public static final TransMethods getByName(String name) { 237 | if (name == null) return null; 238 | return VS.get(name.toLowerCase()); 239 | } 240 | 241 | /** 242 | * 执行此项操作 243 | * 244 | * @param t 处理器 245 | * @param p 玩家 246 | * @param process 过程回调 247 | */ 248 | public void handle(Third t, Player p, BiIntConsumer process) { 249 | val m = TRANS_FUNCS_CALL.get(this); 250 | try { 251 | m.invoke(t, p, process); 252 | } catch (Throwable e) { 253 | e.printStackTrace(); 254 | } 255 | } 256 | } 257 | 258 | /** 259 | * 转换方法 260 | * 261 | * @author yuanlu 262 | */ 263 | @Retention(RUNTIME) 264 | @Target(METHOD) 265 | public @interface TransFunc { 266 | /** @return 对应的转换方法 */ 267 | TransMethods value(); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/java/yuan/plugins/serverDo/bukkit/third/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 第三方插件相关 3 | * 4 | * @author yuanlu 5 | *

6 | * 第三方插件相关 7 | * @author yuanlu 8 | */ 9 | /** 10 | * 第三方插件相关 11 | * 12 | * @author yuanlu 13 | * 14 | */ 15 | package yuan.plugins.serverDo.bukkit.third; 16 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | Prefix: "" 2 | setting: 3 | permission: 4 | no-delay: "yuanlu.serverDo.no-delay" #无延时 5 | no-cooldown: "yuanlu.serverDo.no-cooldown" #无冷却 6 | tp-senior: "yuanlu.serverDo.tpSenior" #高级传送, 包括传送隐身玩家, 跨组传送 7 | tp-other: "yuanlu.serverDo.tpOther" #第三方传送 8 | home: 9 | "default": 3 10 | "10": "yuanlu.serverDo.home.10" 11 | "50": 12 | - "yuanlu.serverDo.home.50" 13 | - "yuanlu.serverDo.home.another-permission-50" 14 | teleport-delay: 3 15 | teleport-cooldown: 100 16 | request-overtime: 120 17 | use-safeLocation: false 18 | #在load步骤加载命令, 此项适用于/tp等命令被ess,CMI等插件覆盖掉时启用 19 | #测试项, 可能会存在问题 20 | preload: true 21 | #back相关设置 22 | back: 23 | #使用tp事件来记录back位置 24 | use-tp-event: false 25 | #使用事件记录传送位置时, 刚加入服务器多少毫秒不记录(防止入服回城等初始传送被错误的记录) 26 | event-after-join: 3000 27 | #使用事件记录传送位置时, 小于此距离(单位: 格, 球形)的传送将被忽略(防止例如限制移动等操作被误记录) 28 | event-ignore-distance: 5 29 | #所有的命令, 删除某个节点将关闭对应的命令 30 | cmd: 31 | reload: 32 | names: "ysd-reload" 33 | tp: 34 | names: 35 | - tp 36 | permission: "yuanlu.serverDo.tp" 37 | description: "tp cmd" 38 | usageMessage: "/tp " 39 | def-cmd: "minecraft:tp" 40 | tpa: 41 | names: 42 | - tpa 43 | permission: "yuanlu.serverDo.tpa" 44 | description: "tpa cmd" 45 | usageMessage: "/tpa " 46 | tphere: 47 | names: 48 | - tphere 49 | permission: "yuanlu.serverDo.tphere" 50 | description: "tphere cmd" 51 | usageMessage: "/tphere " 52 | tpahere: 53 | names: 54 | - tpahere 55 | permission: "yuanlu.serverDo.tpahere" 56 | description: "tpahere cmd" 57 | usageMessage: "/tpahere " 58 | tpaccept: 59 | names: 60 | - tpaccept 61 | permission: "yuanlu.serverDo.tpaccept" 62 | description: "tpaccept cmd" 63 | usageMessage: "/tpaccept [target]" 64 | tpdeny: 65 | names: 66 | - tpdeny 67 | permission: "yuanlu.serverDo.tpdeny" 68 | description: "tpdeny cmd" 69 | usageMessage: "/tpdeny [target]" 70 | tpcancel: 71 | names: 72 | - tpcancel 73 | permission: "yuanlu.serverDo.tpcancel" 74 | description: "tpcancel cmd" 75 | usageMessage: "/tpcancel [target]" 76 | vanish: 77 | names: 78 | - "ysd-v" 79 | permission: "yuanlu.serverDo.vanish" 80 | description: "vanish cmd" 81 | usageMessage: "/ysd-v [always]" 82 | warp: 83 | names: 84 | - "warp" 85 | permission: "yuanlu.serverDo.warp" 86 | description: "warp cmd" 87 | usageMessage: "/warp [name]" 88 | set-warp: 89 | names: 90 | - "setwarp" 91 | permission: "yuanlu.serverDo.set-warp" 92 | description: "set-warp cmd" 93 | usageMessage: "/setwarp " 94 | del-warp: 95 | names: 96 | - "delwarp" 97 | permission: "yuanlu.serverDo.del-warp" 98 | description: "del-warp cmd" 99 | usageMessage: "/delwarp " 100 | home: 101 | names: 102 | - "home" 103 | permission: "yuanlu.serverDo.home" 104 | description: "home cmd" 105 | usageMessage: "/home [name]" 106 | set-home: 107 | names: 108 | - "sethome" 109 | permission: "yuanlu.serverDo.set-home" 110 | description: "set-home cmd" 111 | usageMessage: "/sethome [name]" 112 | del-home: 113 | names: 114 | - "delhome" 115 | permission: "yuanlu.serverDo.del-home" 116 | description: "del-home cmd" 117 | usageMessage: "/delhome " 118 | spawn: 119 | names: 120 | - "spawn" 121 | permission: "yuanlu.serverDo.spawn" 122 | description: "spawn cmd" 123 | usageMessage: "/spawn" 124 | set-spawn: 125 | names: 126 | - "setspawn" 127 | permission: "yuanlu.serverDo.set-spawn" 128 | description: "set-spawn cmd" 129 | usageMessage: "/setspawn" 130 | del-spawn: 131 | names: 132 | - "delspawn" 133 | permission: "yuanlu.serverDo.del-spawn" 134 | description: "del-spawn cmd" 135 | usageMessage: "/delspawn" 136 | trans: 137 | names: 138 | - "ysd-trans" 139 | permission: "yuanlu.serverDo.ysd-trans" 140 | description: "ysd-trans cmd" 141 | usageMessage: "/ysd-trans " 142 | back: 143 | names: 144 | - "back" 145 | permission: "yuanlu.serverDo.back" 146 | description: "back cmd" 147 | usageMessage: "/back" 148 | message: 149 | LanguageFileIsLost: "&c&l[语言文件缺失]节点:%node%" 150 | not-player: "&c您不是玩家!" 151 | no-permission: "&c您没有权限: &7%s" 152 | version-no-recommend: "&c服务器版本不一致: %s <-> %s" 153 | cmd: 154 | help: "&7yuanluServerDo" 155 | back: 156 | non-back: "&c暂时没有保存上一个位置信息" 157 | backing: "&7正在回到上一位置" 158 | tp: 159 | receiver: "" 160 | third-mover: "" 161 | third-target: "" 162 | sender: "&7正在传送至 &2%2$s" 163 | third-tp: "&7正在将 &2%2$s &7传送至 &2%4$s" 164 | help: 165 | - "&7输入指令: &2/tp <玩家> &7传送到某玩家的位置" 166 | - "&7输入指令: &2/tp <玩家1> <玩家2> &7将&2玩家1&7传送到&2玩家2&7的位置" 167 | not-found: "&7未找到玩家 &2%s" 168 | tpa: 169 | receiver: 170 | json: '["",{"text":"&2%2$s &7请求传送到您这里\n"},{"text":"&7若想接受传送,输入 &2/tpaccept\n","clickEvent":{"action":"run_command","value":"/tpaccept %1$s"},"hoverEvent":{"action":"show_text","value":["",{"text":"接受传送","bold":true}]}},{"text":"&7若想拒绝传送,输入 &2/tpdeny\n","clickEvent":{"action":"run_command","value":"/tpdeny %1$s"},"hoverEvent":{"action":"show_text","value":["",{"text":"拒绝传送","bold":true}]}},{"text":"&7此传送请求将在 &2%3$s &7秒后自动超时"}]' 171 | msg: 172 | - "&2%2$s &7请求传送到您这里" 173 | - "&7若想接受传送,输入 &2/tpaccept" 174 | - "&7若想拒绝传送,输入 &2/tpdeny" 175 | - "&7此传送请求将在 &2%3$s &7秒后自动超时" 176 | sender: 177 | json: '["",{"text":"&7请求已发送给 &2%2$s\n"},{"text":"&7想要取消这个传送请求,请输入 &2/tpacancel","clickEvent":{"action":"run_command","value":"/tpcancel %1$s"},"hoverEvent":{"action":"show_text","value":["",{"text":"取消传送","bold":true}]}}]' 178 | msg: 179 | - "请求已发送给 &2%2$s" 180 | - "&7想要取消这个传送请求,请输入 &2/tpacancel" 181 | accept-wait: "&2%2$s &7接受了您的传送请求, 将在 &2%3$s &7秒后启动" 182 | accept: "&7正在传送至 &2%2$s" 183 | deny: "&2%2$s &7拒绝了您的传送请求" 184 | self-tp: "&7您不能向自己发送传送请求" 185 | help: "&7输入指令: &2/tpa <玩家> &7请求传到送某玩家的位置" 186 | cooldown: "&7您还需要等待 &2%.3f &7秒" 187 | not-found: "&7未找到玩家 &2%s&7, 或所在服务器无法传送" 188 | tphere: 189 | receiver: "" 190 | sender: "&7正在将 &2%2$s &7传送到您的位置" 191 | help: "&7输入指令: &2/tphere <玩家> &7传送某玩家到您的位置" 192 | not-found: "&7未找到玩家 &2%s" 193 | tpahere: 194 | sender: 195 | json: '["",{"text":"&7请求已发送给 &2%2$s\n"},{"text":"&7想要取消这个传送请求,请输入 &2/tpacancel","clickEvent":{"action":"run_command","value":"/tpcancel %1$s"},"hoverEvent":{"action":"show_text","value":["",{"text":"点击运行","bold":true}]}}]' 196 | msg: 197 | #备用信息, 对于一些不支持json的版本/模块显示的信息 198 | - "请求已发送给 &2%2$s" 199 | - "&7想要取消这个传送请求,请输入 &2/tpacancel" 200 | receiver: 201 | json: '["",{"text":"&2%2$s &7请求传送您到他那里n"},{"text":"&7若想接受传送,输入 &2/tpaccept\n","clickEvent":{"action":"run_command","value":"/tpaccept %1$s"},"hoverEvent":{"action":"show_text","value":["",{"text":"接受传送","bold":true}]}},{"text":"&7若想拒绝传送,输入 &2/tpdeny\n","clickEvent":{"action":"run_command","value":"/tpdeny %1$s"},"hoverEvent":{"action":"show_text","value":["",{"text":"拒绝传送","bold":true}]}},{"text":"&7此传送请求将在 &2%3$s &7秒后自动超时"}]' 202 | msg: 203 | - "&2%2$s &7请求传送您到他那里" 204 | - "&7若想接受传送,输入 &2/tpaccept" 205 | - "&7若想拒绝传送,输入 &2/tpdeny" 206 | - "&7此传送请求将在 &2%3$s &7秒后自动超时" 207 | accept: "&2%2$s &7已接受请求" 208 | deny: "&2%2$s &7拒绝了您的传送请求" 209 | help: "&7输入指令: &2/tpahere <玩家> &7请求某玩家传送到您的位置" 210 | self-tp: "&7您不能向自己发送传送请求" 211 | cooldown: "&7您还需要等待 &2%.3f &7秒" 212 | not-found: "&7未找到玩家 &2%s&7, 或所在服务器无法传送" 213 | tpaccept: 214 | success: "&7已接受 &2%2$s &7的请求" 215 | success-wait: "&7您接受了 &2%2$s &7的传送请求, 将在 &2%3$s &7秒后启动" 216 | no-request: "&7您没有要接受的请求" 217 | not-found: "&7找不到 &2%s &7的请求" 218 | timeout: "&7最后一个请求已超时 &2%.3f &7秒" 219 | tpdeny: 220 | success: "&7您拒绝了 &2%2$s &7的传送请求" 221 | no-request: "&7您没有要接受的请求" 222 | not-found: "&7找不到 &2%s &7的请求" 223 | timeout: "&7最后一个请求已超时 &2%.3f &7秒" 224 | tpcancel: 225 | remote-fail: "&c由于未知原因, &2%s &c试图取消一个不存在的请求" 226 | remote-success: "&2%2$s &7取消了传送" 227 | success: "&7你取消了发送给 &2%2$s &7的传送请求" 228 | vanish: 229 | always-hide: "&7已 &2开启 &7自动隐藏您的身份" 230 | always-show: "&7已 &2关闭 &7自动隐藏您的身份" 231 | auto-hide: "&7已自动隐藏您的身份" 232 | hide: "&7已在列表中 &2隐藏 &7您的身份" 233 | show: "&7已在列表中 &2显示 &7您的身份" 234 | help: 235 | - "&7输入指令: &2/ysd-v &7切换YSD隐身状态" 236 | - "&7输入指令: &2/ysd-v always &7切换YSD自动隐身状态" 237 | reload: 238 | success: "&c成功重载(此功能为实验性功能, 若出现任何错误请使用服务器重载指令或重启服务器)" 239 | only-console: "&7请在控制台使用此指令" 240 | warp: 241 | list: 242 | - "%s" 243 | - "%s" 244 | list-w1: "&7同组地标(&2%2$s&7): %1$s&7" 245 | list-w2: "&7跨组地标(&2%2$s&7): %1$s&7" 246 | list-element: "&2%2$s" 247 | list-delimiter: "&7, " 248 | tp: "&7正在传送至地标 &2%s&7(&2%s&7)" 249 | not-found: "&7找不到地标 &2%s" 250 | del-warp: 251 | success: "&7成功删除地标 &2%s" 252 | fail: "&7找不到地标 &2%s" 253 | help: "&7输入指令: &2/delwarp &7删除地标" 254 | set-warp: 255 | success: "&7成功设置地标 &2%s" 256 | help: "&7输入指令: &2/setwarp &7设置地标" 257 | home: 258 | list: 259 | - "%s" 260 | - "%s" 261 | list-w1: "&7同组家(&2%2$s&7): %1$s&7" 262 | list-w2: "&7跨组家(&2%2$s&7): %1$s&7" 263 | list-element: "&2%2$s" 264 | list-delimiter: "&7, " 265 | tp: "&7正在传送至家 &2%s&7(&2%s&7)" 266 | not-found: "&7找不到家 &2%s" 267 | del-home: 268 | success: "&7成功删除家 &2%s" 269 | fail: "&7找不到家 &2%s" 270 | help: "&7输入指令: &2/delhome &7删除家" 271 | set-home: 272 | success: "&7成功设置家 &2%s" 273 | fail: "&7家数量已达上限: &2%s" 274 | spawn: 275 | tp: "&7正在传送至出生点 &2%s&7(&2%s&7)" 276 | not-found: "&7找不到出生点 &2%s" 277 | del-spawn: 278 | success: "&7成功删除出生点 &2%s" 279 | fail: "&7找不到出生点 &2%s" 280 | set-spawn: 281 | success: "&7成功设置出生点 &2%s" 282 | trans: 283 | help: "&7输入指令:&2/ysd-trans &7从某插件转换数据" 284 | not-found: "&7找不到名为 &2&s &7的转换器" 285 | invlid: "&7转换器 &2%s &7不可用" 286 | cannot: "&7转换器 &2%s &7不可以完成 &2&s &7的功能" 287 | title: "&7转换器 &2%s &7运行中" 288 | sub-title: "&7进度: &6%s&7/&2%s&r" 289 | success: "&7已完成转换: 完成 &2%s &7(预计 &2%s &7)" 290 | help-CMI: 291 | - "&2CMI &7转换器可用功能: " 292 | - "&7 - &2all-home &7转换全部的家数据" 293 | basic: 294 | message-time-out: "&c服务器通讯超时, 请联系管理员" 295 | version-bad: "&c服务器版本检查失败, 请联系管理员" 296 | bungee-error: "&cBC服务器发生错误, 请联系管理员" 297 | bungee-player-offline: "&7对方已离线" 298 | tpcancel-move: "&7由于移动,传送任务被取消" 299 | -------------------------------------------------------------------------------- /yuanluServerDo-bukkit/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: yuanluServerDo 2 | main: yuan.plugins.serverDo.bukkit.Main 3 | version: ${parent.version} 4 | description: ${parent.description} 5 | author: yuanlu 6 | website: ${parent.url} 7 | api-version: 1.13 8 | 9 | # softdepend: ['CMI','CMILib','Essentials'] 10 | loadbefore: [ 'CMI','CMILib','Essentials' ] 11 | -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bid.yuanlu 9 | yuanluServerDo 10 | 1.2.2 11 | 12 | 13 | yuanluServerDo-bungeecord 14 | jar 15 | 16 | YuanluServerDo BungeeCord 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-shade-plugin 27 | 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | src/main/resources 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | sonatype 47 | https://oss.sonatype.org/content/groups/public/ 48 | 49 | 50 | 51 | 52 | 53 | net.md-5 54 | bungeecord-api 55 | 1.16-R0.5-SNAPSHOT 56 | provided 57 | 58 | 59 | bid.yuanlu 60 | yuanluServerDo-common 61 | ${project.version} 62 | compile 63 | 64 | 65 | 66 | org.bstats 67 | bstats-bungeecord 68 | 3.0.0 69 | compile 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/src/main/java/yuan/plugins/serverDo/bungee/ConfigManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bungee; 5 | 6 | import com.google.common.base.Objects; 7 | import lombok.*; 8 | import net.md_5.bungee.api.connection.ProxiedPlayer; 9 | import net.md_5.bungee.api.connection.Server; 10 | import net.md_5.bungee.config.Configuration; 11 | import net.md_5.bungee.config.ConfigurationProvider; 12 | import net.md_5.bungee.config.YamlConfiguration; 13 | import yuan.plugins.serverDo.*; 14 | import yuan.plugins.serverDo.Tool.ThrowableFunction; 15 | import yuan.plugins.serverDo.Tool.ThrowableRunnable; 16 | 17 | import java.io.*; 18 | import java.util.*; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | 21 | /** 22 | * 配置管理器 23 | * 24 | * @author yuanlu 25 | */ 26 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 27 | public final class ConfigManager { 28 | /** 地标点 */ 29 | static final HashMap WARPS = new LinkedHashMap<>(); 30 | /** 家点 */ 31 | static final HomesLRU HOMES = new HomesLRU(32); 32 | /** 服务器组 */ 33 | private static final HashMap> GROUPS = new HashMap<>(); 34 | /** 禁用的服务器 */ 35 | private static final HashSet BAN_SERVER = new HashSet<>(); 36 | /** 配置文件 */ 37 | private static @Getter Configuration config; 38 | /** tab替换 */ 39 | private static @Getter 40 | @Setter String tabReplace; 41 | /** 服务器信息 */ 42 | private static byte[] serverInfo; 43 | /** 出错 */ 44 | private static @Getter boolean errorGroup; 45 | /** 自动保存延时 */ 46 | private @Getter 47 | @Setter 48 | static long saveDelay = 1000 * 60; 49 | /** 是否使用AT功能 */ 50 | private @Getter 51 | @Setter 52 | static boolean useAt = true; 53 | 54 | /** 55 | * 检测是否启用服务器 56 | * 57 | * @param server 服务器 58 | * 59 | * @return 此服务器是否启用本插件 60 | */ 61 | public static boolean allowServer(String server) { 62 | return !BAN_SERVER.contains(server); 63 | } 64 | 65 | /** 66 | * 检测是否可以传送 67 | * 68 | * @param s1 服务器1 69 | * @param s2 服务器2 70 | * 71 | * @return 是否可以传送 72 | */ 73 | public static boolean canTp(String s1, String s2) { 74 | if (BAN_SERVER.contains(s1) || BAN_SERVER.contains(s2)) return false; 75 | if (Objects.equal(s1, s2)) return true; 76 | if (errorGroup) { 77 | if (Main.isDEBUG()) Main.getMain().getLogger().warning("error: 服务器组不存在"); 78 | return false; 79 | } 80 | val group = GROUPS.get(s1); 81 | return group != null && group.contains(s2); 82 | } 83 | 84 | /** 85 | * 关闭时保存 86 | */ 87 | public static void closeSave() { 88 | val list = new ArrayList<>(ConfFile.SAVE_DELAY.keySet()); 89 | ConfFile.SAVE_DELAY.clear(); 90 | list.forEach(ConfFile::save); 91 | for (val v : PlayerConfFile.values()) { 92 | if (v.SAVE_DELAY.isEmpty()) continue; 93 | val l = new ArrayList<>(v.SAVE_DELAY.keySet()); 94 | v.SAVE_DELAY.clear(); 95 | l.forEach(v::save); 96 | } 97 | } 98 | 99 | /** 100 | * 初始化 101 | * 102 | * @param config config 103 | */ 104 | public static void init(Configuration config) { 105 | ConfigManager.config = config; 106 | val tabReplace = config.getString("player-tab-replace", "yl★:" + Tool.randomString(8)); 107 | setTabReplace(tabReplace); 108 | setSaveDelay(config.getLong("save-delay", getSaveDelay())); 109 | setUseAt(config.getBoolean("use-at", isUseAt())); 110 | loadGroup(config); 111 | 112 | serverInfo = Channel.ServerInfo.sendS(tabReplace, Main.getMain().getDescription().getVersion(), Channel.ServerInfo.ServerPkg.ProxyType.BungeeCord); 113 | 114 | Arrays.stream(ConfFile.values()).forEach(ConfFile::load); 115 | 116 | } 117 | 118 | /** 119 | * 加载组 120 | * 121 | * @param config 配置文件 122 | */ 123 | private static void loadGroup(Configuration config) { 124 | val sg = config.getSection("server-group"); 125 | if (sg == null) { 126 | errorGroup = true; 127 | Main.getMain().getLogger().warning("[SERVER GROUP] config error!"); 128 | return; 129 | } 130 | for (val key : sg.getKeys()) { 131 | val group = sg.getStringList(key); 132 | for (val server : group) { 133 | HashSet canTp = GROUPS.computeIfAbsent(server, k -> new HashSet<>()); 134 | canTp.addAll(group); 135 | } 136 | if (ShareData.isDEBUG()) ShareData.getLogger().info("加载组 " + key + ": " + group); 137 | } 138 | 139 | BAN_SERVER.clear(); 140 | BAN_SERVER.addAll(config.getStringList("server-ban")); 141 | } 142 | 143 | /** 144 | * 保存配置
145 | * 将会延时保存 146 | * 147 | * @param f 配置类型 148 | */ 149 | public static void saveConf(ConfFile f) { 150 | WaitMaintain.put(ConfFile.SAVE_DELAY, f, System.currentTimeMillis(), saveDelay, f::save); 151 | } 152 | 153 | /** 154 | * 保存配置
155 | * 将会延时保存 156 | * 157 | * @param f 配置类型 158 | * @param player 对应玩家 159 | * 160 | * @see #saveConf(PlayerConfFile, UUID) 161 | */ 162 | public static void saveConf(PlayerConfFile f, ProxiedPlayer player) { 163 | saveConf(f, player.getUniqueId()); 164 | } 165 | 166 | /** 167 | * 保存配置
168 | * 将会延时保存 169 | * 170 | * @param f 配置类型 171 | * @param u 对应玩家UUID 172 | */ 173 | public static void saveConf(PlayerConfFile f, UUID u) { 174 | f.needSave.add(u); 175 | WaitMaintain.put(f.SAVE_DELAY, u, System.currentTimeMillis(), saveDelay, () -> f.save(u)); 176 | } 177 | 178 | /** 179 | * 发送BC信息给子服务器 180 | * 181 | * @param server 服务器 182 | */ 183 | public static void sendBungeeInfoToServer(Server server) { 184 | Main.send(server, serverInfo); 185 | } 186 | 187 | /** 188 | * 配置文件 189 | * 190 | * @author yuanlu 191 | */ 192 | @Getter 193 | @AllArgsConstructor 194 | public enum ConfFile { 195 | /** 自动隐身 */ 196 | ALWAYS_VANISH("alwaysvanish.uid") { 197 | @Override 198 | protected void load0() throws IOException { 199 | try (BufferedReader in = new BufferedReader(new FileReader(getFile()))) { 200 | in.lines().forEach(s -> { 201 | try { 202 | Core.alwaysVanish.add(UUID.fromString(s)); 203 | } catch (IllegalArgumentException e) { 204 | ShareData.getLogger().warning("[Conf] " + fname + ": Bad UUID: " + s); 205 | e.printStackTrace(); 206 | } 207 | }); 208 | } 209 | } 210 | 211 | @Override 212 | protected void save0() throws IOException { 213 | try (BufferedWriter out = new BufferedWriter(new FileWriter(getFile()))) { 214 | for (UUID u : Core.alwaysVanish) { 215 | out.write(u.toString()); 216 | out.write('\n'); 217 | } 218 | } 219 | } 220 | 221 | }, 222 | /** 传送地标 */ 223 | WARP("warp.yml") { 224 | @Override 225 | protected void load0() throws IOException { 226 | Configuration warps = YAML.load(getFile()); 227 | for (String name : warps.getKeys()) { 228 | Configuration warp = warps.getSection(name); 229 | 230 | String world = warp.getString("world", null); 231 | String server = warp.getString("server", null); 232 | double x = warp.getDouble("x", Double.NaN); 233 | double y = warp.getDouble("y", Double.NaN); 234 | double z = warp.getDouble("z", Double.NaN); 235 | float Y = warp.getFloat("yaw", Float.NaN); 236 | float P = warp.getFloat("pitch", Float.NaN); 237 | if (world == null || server == null || Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Float.isNaN(Y) || Float.isNaN(P)) { 238 | ShareData.getLogger().warning(String.format("[WARPS] 错误的warp数据: %s %s [%s, %s, %s] [%s,%s]", server, world, x, y, z, Y, P)); 239 | } else { 240 | WARPS.put(name, new ShareLocation(x, y, z, Y, P, world, server)); 241 | } 242 | } 243 | } 244 | 245 | @Override 246 | protected void save0() throws IOException { 247 | Configuration warps = new Configuration(); 248 | for (Map.Entry e : WARPS.entrySet()) { 249 | String name = e.getKey(); 250 | ShareLocation warp = e.getValue(); 251 | warps.set(name + ".world", warp.getWorld()); 252 | warps.set(name + ".server", warp.getServer()); 253 | warps.set(name + ".x", warp.getX()); 254 | warps.set(name + ".y", warp.getY()); 255 | warps.set(name + ".z", warp.getZ()); 256 | warps.set(name + ".yaw", warp.getYaw()); 257 | warps.set(name + ".pitch", warp.getPitch()); 258 | } 259 | YAML.save(warps, getFile()); 260 | } 261 | 262 | }; 263 | 264 | /** Yaml处理器 */ 265 | protected static final ConfigurationProvider YAML = ConfigurationProvider.getProvider(YamlConfiguration.class); 266 | /** 保存延时 */ 267 | private static final EnumMap SAVE_DELAY = new EnumMap<>(ConfFile.class); 268 | /** 文件名 */ 269 | protected final String fname; 270 | 271 | /** @return file */ 272 | public File getFile() { 273 | val folder = Main.getMain().getDataFolder(); 274 | return new File(folder, fname); 275 | } 276 | 277 | /** 加载 */ 278 | protected void load() { 279 | try { 280 | load0(); 281 | } catch (FileNotFoundException e) { 282 | // ignore 283 | } catch (IOException e) { 284 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 285 | e.printStackTrace(); 286 | } 287 | } 288 | 289 | /** 290 | * 实际加载 291 | * 292 | * @throws IOException IOE 293 | */ 294 | protected abstract void load0() throws IOException; 295 | 296 | /** 保存 */ 297 | protected void save() { 298 | try { 299 | save0(); 300 | } catch (IOException e) { 301 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 302 | e.printStackTrace(); 303 | } 304 | } 305 | 306 | /** 307 | * 实际保存 308 | * 309 | * @throws IOException IOE 310 | */ 311 | protected abstract void save0() throws IOException; 312 | } 313 | 314 | /** 315 | * 玩家配置文件
316 | * load: 被动式加载, 由LRU调用, 通过 {@link #load(Object, ThrowableFunction) 框架函数} 317 | * 调用实际加载函数, 加载数据
318 | * save: 被动式保存, 由LRU调用, 通过 {@link #save(UUID, ThrowableRunnable) 框架函数} 调用实际保存函数, 319 | * 保存数据
320 | * save: 主动式保存, 通过 {@link #save(UUID)} 触发, 由具体配置指定LRU, 调用其保存函数 321 | * 322 | * @author yuanlu 323 | */ 324 | @Getter 325 | @AllArgsConstructor 326 | public enum PlayerConfFile { 327 | /** 传送家 */ 328 | HOME("home.yml") { 329 | @Override 330 | protected void save(UUID u) { 331 | HashMap map = HOMES.check(u); 332 | if (map != null) HOMES.clearHandle(u, map); 333 | } 334 | }; 335 | 336 | /** Yaml处理器 */ 337 | protected static final ConfigurationProvider YAML = ConfigurationProvider.getProvider(YamlConfiguration.class); 338 | /** 文件名 */ 339 | protected final String fname; 340 | /** 需要保存 */ 341 | protected final HashSet needSave = new HashSet<>(); 342 | /** 保存延时 */ 343 | private final ConcurrentHashMap SAVE_DELAY = new ConcurrentHashMap<>(); 344 | 345 | /** 346 | * @param u UUID 347 | * @param mk 是否创建文件夹 348 | * 349 | * @return file 350 | */ 351 | public File getFile(UUID u, boolean mk) { 352 | File folder = Main.getMain().getDataFolder(); 353 | val uuid = u.toString(); 354 | folder = new File(folder, uuid.substring(0, 2)); 355 | folder = new File(folder, uuid); 356 | if (mk) folder.mkdirs(); 357 | return new File(folder, fname); 358 | } 359 | 360 | /** 361 | * 加载 362 | * 363 | * @param T 364 | * @param R 365 | * @param t 输入数据 366 | * @param r 运行体 367 | * 368 | * @return result 369 | */ 370 | protected R load(T t, ThrowableFunction r) { 371 | try { 372 | return r.apply(t); 373 | } catch (FileNotFoundException e) { 374 | // ignore 375 | } catch (IOException e) { 376 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 377 | e.printStackTrace(); 378 | } 379 | return null; 380 | } 381 | 382 | /** 383 | * 强制保存
384 | * 由配置文件实现 385 | * 386 | * @param u UUID 387 | */ 388 | protected abstract void save(UUID u); 389 | 390 | /** 391 | * 保存 392 | * 393 | * @param u UUID 394 | * @param r 运行体 395 | */ 396 | protected void save(UUID u, ThrowableRunnable r) { 397 | if (!needSave.remove(u)) return; 398 | try { 399 | r.run(); 400 | } catch (FileNotFoundException e) { 401 | // ignore 402 | } catch (IOException e) { 403 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 404 | e.printStackTrace(); 405 | } 406 | } 407 | } 408 | 409 | /** 410 | * Home 411 | * 412 | * @author yuanlu 413 | */ 414 | public static final class HomesLRU extends LRUCache> { 415 | 416 | /** 配置文件 */ 417 | private static final PlayerConfFile HOME = PlayerConfFile.HOME; 418 | 419 | /** @param size size */ 420 | private HomesLRU(int size) { 421 | super(size); 422 | } 423 | 424 | @Override 425 | protected void clearHandle(UUID k, HashMap v) { 426 | HOME.save(k, () -> save0(k, v)); 427 | } 428 | 429 | @Override 430 | protected HashMap create(UUID k) { 431 | HashMap data = HOME.load(k, this::load0); 432 | return data == null ? new HashMap<>() : data; 433 | } 434 | 435 | /** 436 | * load 437 | * 438 | * @param uid UUID 439 | * 440 | * @return data 441 | * 442 | * @throws IOException IOE 443 | */ 444 | private HashMap load0(@NonNull UUID uid) throws IOException { 445 | HashMap m = new HashMap<>(); 446 | val f = HOME.getFile(uid, false); 447 | val warps = PlayerConfFile.YAML.load(f); 448 | for (val name : warps.getKeys()) { 449 | val warp = warps.getSection(name); 450 | 451 | val world = warp.getString("world", null); 452 | val server = warp.getString("server", null); 453 | val x = warp.getDouble("x", Double.NaN); 454 | val y = warp.getDouble("y", Double.NaN); 455 | val z = warp.getDouble("z", Double.NaN); 456 | val Y = warp.getFloat("yaw", Float.NaN); 457 | val P = warp.getFloat("pitch", Float.NaN); 458 | if (world == null || server == null || Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Float.isNaN(Y) || Float.isNaN(P)) { 459 | ShareData.getLogger() 460 | .warning(String.format("[HOMES] 错误的home数据: %s: %s %s [%s, %s, %s] [%s,%s]", f.getName(), server, world, x, y, z, Y, P)); 461 | } else { 462 | m.put(name, new ShareLocation(x, y, z, Y, P, world, server)); 463 | } 464 | } 465 | return m; 466 | } 467 | 468 | /** 469 | * save 470 | * 471 | * @param uid uuid 472 | * @param map data 473 | * 474 | * @throws IOException IOE 475 | */ 476 | private void save0(@NonNull UUID uid, HashMap map) throws IOException { 477 | val warps = new Configuration(); 478 | for (val e : map.entrySet()) { 479 | val name = e.getKey(); 480 | val warp = e.getValue(); 481 | warps.set(name + ".world", warp.getWorld()); 482 | warps.set(name + ".server", warp.getServer()); 483 | warps.set(name + ".x", warp.getX()); 484 | warps.set(name + ".y", warp.getY()); 485 | warps.set(name + ".z", warp.getZ()); 486 | warps.set(name + ".yaw", warp.getYaw()); 487 | warps.set(name + ".pitch", warp.getPitch()); 488 | } 489 | PlayerConfFile.YAML.save(warps, PlayerConfFile.HOME.getFile(uid, true)); 490 | } 491 | } 492 | } 493 | -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/src/main/java/yuan/plugins/serverDo/bungee/Core.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月11日 4 | * file: Core.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.bungee; 9 | 10 | import lombok.AccessLevel; 11 | import lombok.NoArgsConstructor; 12 | import lombok.NonNull; 13 | import lombok.val; 14 | import net.md_5.bungee.api.config.ServerInfo; 15 | import net.md_5.bungee.api.connection.ProxiedPlayer; 16 | import net.md_5.bungee.api.connection.Server; 17 | import net.md_5.bungee.api.event.ServerConnectEvent.Reason; 18 | import yuan.plugins.serverDo.Channel; 19 | import yuan.plugins.serverDo.ShareData; 20 | import yuan.plugins.serverDo.ShareLocation; 21 | import yuan.plugins.serverDo.Tool; 22 | import yuan.plugins.serverDo.bungee.ConfigManager.ConfFile; 23 | import yuan.plugins.serverDo.bungee.ConfigManager.PlayerConfFile; 24 | 25 | import java.util.HashMap; 26 | import java.util.HashSet; 27 | import java.util.UUID; 28 | import java.util.function.Function; 29 | 30 | /** 31 | * BC端核心 32 | * 33 | * @author yuanlu 34 | */ 35 | @NoArgsConstructor(access = AccessLevel.PACKAGE) 36 | public class Core { 37 | /** 版本不正确的服务器 */ 38 | static final HashSet BAD_SERVER = new HashSet<>(); 39 | /** 时间戳修正 */ 40 | static final HashMap TIME_AMEND = new HashMap<>(); 41 | 42 | /** 时间修正回调等待 */ 43 | static final HashMap TIME_AMEND_WAITER = new HashMap<>(); 44 | 45 | /** 当前隐身 */ 46 | static final HashSet nowVanish = new HashSet<>(); 47 | /** 自动隐身 */ 48 | static final HashSet alwaysVanish = new HashSet<>(); 49 | 50 | /** 51 | * 自动隐身处理 52 | * 53 | * @param player 玩家 54 | * @param server 玩家所在服务器 55 | */ 56 | public static void autoVanish(ProxiedPlayer player, Server server) { 57 | val u = player.getUniqueId(); 58 | if (alwaysVanish.contains(u)) { 59 | nowVanish.add(u); 60 | Main.send(server, Channel.Vanish.sendC(true)); 61 | } 62 | } 63 | 64 | /** 65 | * 检查TP 66 | * 67 | * @param isSenior 是否是高级传送 68 | * @param myServer 本服务器 69 | * @param target 目标玩家 70 | * 71 | * @return 是否可以传送 72 | */ 73 | public static boolean canTp(boolean isSenior, String myServer, ProxiedPlayer target) { 74 | val targetServer = target.getServer().getInfo().getName(); 75 | if (!ConfigManager.allowServer(targetServer)) return false; 76 | if (isSenior) return true; 77 | 78 | if (!ConfigManager.canTp(myServer, targetServer) || hasVanish(target)) return false; 79 | 80 | return true; 81 | } 82 | 83 | /** 84 | * 检查服务器间是否可以传送 85 | * 86 | * @param myServer 本服务器 87 | * @param targetServer 目标服务器 88 | * 89 | * @return 是否可以传送 90 | */ 91 | public static boolean canTp(String myServer, String targetServer) { 92 | if (!ConfigManager.allowServer(targetServer) || !ConfigManager.canTp(myServer, targetServer)) return false; 93 | return true; 94 | } 95 | 96 | /** 97 | * 获取地标 98 | * 99 | * @param player 玩家 100 | * @param name 名称 101 | * 102 | * @return 坐标 103 | */ 104 | public static ShareLocation getHome(@NonNull ProxiedPlayer player, @NonNull String name) { 105 | return ConfigManager.HOMES.get(player.getUniqueId()).get(name); 106 | } 107 | 108 | /** 109 | * 获取家集合 110 | * 111 | * @param player 玩家 112 | * 113 | * @return homes 114 | */ 115 | public static HashMap getHomes(@NonNull ProxiedPlayer player) { 116 | return ConfigManager.HOMES.get(player.getUniqueId()); 117 | } 118 | 119 | /** 120 | * 获取本服务器比子服务器快多少毫秒 121 | * 122 | * @param server 服务器 123 | * 124 | * @return 时间戳修正 125 | */ 126 | public static long getTimeAmend(ServerInfo server) { 127 | if (server == null) return 0; 128 | val amend = TIME_AMEND.get(server.getName()); 129 | return amend == null ? 0 : amend; 130 | } 131 | 132 | /** 133 | * 获取地标 134 | * 135 | * @param name 名称 136 | * 137 | * @return 坐标 138 | */ 139 | public static ShareLocation getWarp(@NonNull String name) { 140 | return ConfigManager.WARPS.get(name); 141 | } 142 | 143 | /** 144 | * 是否拥有隐身 145 | * 146 | * @param player 玩家 147 | * 148 | * @return 是否拥有隐身 149 | */ 150 | public static boolean hasVanish(ProxiedPlayer player) { 151 | return nowVanish.add(player.getUniqueId()); 152 | } 153 | 154 | /** 155 | * 搜索家 156 | * 157 | * @param player 玩家 158 | * @param name 搜索名 159 | * 160 | * @return 匹配名 161 | */ 162 | public static String searchHome(@NonNull ProxiedPlayer player, @NonNull String name) { 163 | return Tool.search(name, ConfigManager.HOMES.get(player.getUniqueId()).keySet().iterator()); 164 | } 165 | 166 | /** 167 | * 搜索地标 168 | * 169 | * @param name 搜索名 170 | * 171 | * @return 匹配名 172 | */ 173 | public static String searchWarp(@NonNull String name) { 174 | return Tool.search(name, ConfigManager.WARPS.keySet().iterator()); 175 | } 176 | 177 | /** 178 | * 设置/删除家 179 | * 180 | * @param player 玩家 181 | * @param name 家名称 182 | * @param loc 家坐标 183 | * @param amount 家最大数量 184 | * 185 | * @return true: 成功删除/成功设置
186 | * false:不存在/已满 187 | */ 188 | public static boolean setHome(@NonNull ProxiedPlayer player, @NonNull String name, ShareLocation loc, int amount) { 189 | val home = ConfigManager.HOMES.get(player.getUniqueId()); 190 | boolean result; 191 | if (loc == null) result = home.remove(name) != null; 192 | else if ((loc = loc.clone()).getServer() == null) throw new IllegalArgumentException("[HOME] Null sever: " + name); 193 | else { 194 | result = (home.containsKey(name) || home.size() < amount); 195 | if (result) home.put(name, loc); 196 | } 197 | 198 | ConfigManager.saveConf(PlayerConfFile.HOME, player); 199 | return result; 200 | } 201 | 202 | /** 203 | * 设置家
204 | * 用于转换时上传 205 | * 206 | * @param player 玩家 207 | * @param name 家名称 208 | * @param loc 家坐标 209 | * 210 | * @see TransHandler#receiveHome(ProxiedPlayer, 211 | * yuan.plugins.serverDo.Channel.TransHome) 212 | */ 213 | public static void setHome(@NonNull UUID player, @NonNull String name, @NonNull ShareLocation loc) { 214 | val home = ConfigManager.HOMES.get(player); 215 | if ((loc = loc.clone()).getServer() == null) throw new IllegalArgumentException("[HOME] Null sever: " + name); 216 | else home.put(name, loc); 217 | 218 | ConfigManager.saveConf(PlayerConfFile.HOME, player); 219 | } 220 | 221 | /** 222 | * 设置/删除地标 223 | * 224 | * @param name 地标名称 225 | * @param loc 地标坐标 226 | * 227 | * @return true: 成功删除/覆盖
228 | * false:不存在/新建 229 | */ 230 | public static boolean setWarp(@NonNull String name, ShareLocation loc) { 231 | boolean result; 232 | if (loc == null) result = ConfigManager.WARPS.remove(name) != null; 233 | else if ((loc = loc.clone()).getServer() == null) throw new IllegalArgumentException("[WARP] Null sever: " + name); 234 | else result = ConfigManager.WARPS.put(name, loc) != null; 235 | ConfigManager.saveConf(ConfFile.WARP); 236 | return result; 237 | } 238 | 239 | /** 240 | * 开始时间修正检测 241 | */ 242 | public static void startTimeAmend() { 243 | val pack = Channel.TimeAmend.sendC(); 244 | for (val server : Main.getMain().getProxy().getServers().values()) { 245 | if (server.getPlayers().isEmpty()) continue; 246 | TIME_AMEND_WAITER.put(server.getName(), System.currentTimeMillis()); 247 | Main.send(server, pack); 248 | } 249 | } 250 | 251 | /** 252 | * 切换隐身 253 | * 254 | * @param player 玩家 255 | * @param isAlways 是否自动隐身 256 | * 257 | * @return 切换后状态 258 | */ 259 | public static boolean switchVanish(ProxiedPlayer player, boolean isAlways) { 260 | val set = isAlways ? alwaysVanish : nowVanish; 261 | val u = player.getUniqueId(); 262 | boolean r; 263 | if (!(r = set.add(u))) set.remove(u); 264 | ConfigManager.saveConf(ConfFile.ALWAYS_VANISH); 265 | return r; 266 | } 267 | 268 | /** 269 | * 时间修正回调 270 | * 271 | * @param info 子服务器 272 | * @param client 子服务器时间 273 | */ 274 | public static void timeAmendCallback(ServerInfo info, long client) { 275 | val start = TIME_AMEND_WAITER.remove(info.getName()); 276 | if (start == null) return; 277 | val end = System.currentTimeMillis(); 278 | final long time = end - start; 279 | if (time > 100) { 280 | ShareData.getLogger().warning("[时间修正] " + info.getName() + " 通信时间过长: " + time); 281 | return; 282 | } 283 | long shift = end - (client + time / 2); 284 | TIME_AMEND.put(info.getName(), shift); 285 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[时间修正] " + info.getName() + " 偏移量:" + shift + ", 通信时长: " + time); 286 | } 287 | 288 | /** 289 | * 传送坐标 290 | * 291 | * @param player 玩家 292 | * @param loc 目标点 293 | * @param callback 传送回调数据生成 294 | */ 295 | public static void tpLocation(@NonNull ProxiedPlayer player, ShareLocation loc, @NonNull Function callback) { 296 | val nowServer = player.getServer(); 297 | val targetServer = loc == null ? null : Main.getMain().getProxy().getServerInfo(loc.getServer()); 298 | if (targetServer == null) { 299 | Main.send(nowServer, callback.apply(false)); 300 | return; 301 | } 302 | Main.sendQueue(targetServer, Channel.TpLoc.s1S_tpLoc(loc, player.getName())); 303 | if (nowServer.getInfo().getName().equals(targetServer.getName())) { 304 | Main.send(nowServer, callback.apply(true)); 305 | } else player.connect(targetServer, (success, e) -> { 306 | Main.send(nowServer, callback.apply(success)); 307 | if (e != null) e.printStackTrace(); 308 | }, Reason.COMMAND); 309 | } 310 | 311 | } 312 | -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/src/main/java/yuan/plugins/serverDo/bungee/TabHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bungee; 5 | 6 | import lombok.AccessLevel; 7 | import lombok.NoArgsConstructor; 8 | import lombok.val; 9 | import net.md_5.bungee.api.connection.ProxiedPlayer; 10 | import net.md_5.bungee.api.connection.Server; 11 | import net.md_5.bungee.api.event.TabCompleteResponseEvent; 12 | import yuan.plugins.serverDo.ShareData.TabType; 13 | 14 | import java.util.List; 15 | import java.util.stream.Stream; 16 | 17 | /** 18 | * tab处理器 19 | * 20 | * @author yuanlu 21 | */ 22 | @SuppressWarnings("javadoc") 23 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 24 | public final class TabHandler { 25 | private static void onAt(TabCompleteResponseEvent e, String request, List list) { 26 | if (!ConfigManager.isUseAt()) { 27 | list.add("@" + request); 28 | return; 29 | } 30 | 31 | Stream stream = Main.getMain().getProxy().getPlayers()// 32 | .stream()// 33 | .map(ProxiedPlayer::getName)// 34 | .map(String::toLowerCase); 35 | if (!request.isEmpty()) stream = stream.filter(request::startsWith); 36 | 37 | stream.map("@"::concat).forEach(list::add); 38 | } 39 | 40 | private static void onHome(TabCompleteResponseEvent e, String request, List list) { 41 | val server = (Server) e.getSender(); 42 | val player = (ProxiedPlayer) e.getReceiver(); 43 | if (server == null || player == null) return; 44 | val serverName = server.getInfo().getName(); 45 | Core.getHomes(player).forEach((name, loc) -> { 46 | if (name.toLowerCase().startsWith(request) && Core.canTp(serverName, loc.getServer()))// 47 | list.add(name); 48 | }); 49 | } 50 | 51 | private static void onTp(TabCompleteResponseEvent e, String request, List list, boolean isAll) { 52 | val server = (Server) e.getSender(); 53 | if (server == null) return; 54 | val target = server.getInfo().getName(); 55 | if (ConfigManager.allowServer(target)) for (val p : Main.getMain().getProxy().getPlayers()) { 56 | val name = p.getName(); 57 | if (name.toLowerCase().startsWith(request) && Core.canTp(isAll, target, p)) // 58 | list.add(name); 59 | } 60 | } 61 | 62 | private static void onWarp(TabCompleteResponseEvent e, String request, List list) { 63 | val server = (Server) e.getSender(); 64 | if (server == null) return; 65 | val serverName = server.getInfo().getName(); 66 | ConfigManager.WARPS.forEach((name, loc) -> { 67 | if (name.toLowerCase().startsWith(request) && Core.canTp(serverName, loc.getServer())) list.add(name); 68 | }); 69 | } 70 | 71 | /** 72 | * EVENT 73 | * 74 | * @param e 补全响应 75 | */ 76 | public static void TabCompleteResponse(TabCompleteResponseEvent e) { 77 | val list = e.getSuggestions(); 78 | if (list.size() != 1) return; 79 | val str = list.get(0); 80 | if (str != null) { 81 | val type = TabType.getType(ConfigManager.getTabReplace(), str); 82 | val request = TabType.getValue(ConfigManager.getTabReplace(), str); 83 | if (type == null || request == null) return; 84 | list.clear(); 85 | switch (type) { 86 | case TP_ALL: 87 | onTp(e, request, list, true); 88 | break; 89 | case TP_NORMAL: 90 | onTp(e, request, list, false); 91 | break; 92 | case WARP: 93 | onWarp(e, request, list); 94 | break; 95 | case HOME: 96 | onHome(e, request, list); 97 | break; 98 | case AT: 99 | onAt(e, request, list); 100 | break; 101 | } 102 | if (list.isEmpty()) list.add(request); 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/src/main/java/yuan/plugins/serverDo/bungee/TransHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.bungee; 5 | 6 | import lombok.AccessLevel; 7 | import lombok.NoArgsConstructor; 8 | import net.md_5.bungee.api.connection.ProxiedPlayer; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.TransHome; 11 | import yuan.plugins.serverDo.Channel.TransWarp; 12 | import yuan.plugins.serverDo.ShareLocation; 13 | 14 | import java.util.HashMap; 15 | import java.util.LinkedHashMap; 16 | import java.util.UUID; 17 | import java.util.function.Function; 18 | 19 | /** 20 | * 转换处理器 21 | * 22 | * @author yuanlu 23 | */ 24 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 25 | public final class TransHandler { 26 | /** 所有接收到的家 */ 27 | private static final HashMap> HOMES = new HashMap<>(); 28 | /** 所有接收到的家 */ 29 | private static final Function> HOMES_MF = x -> new LinkedHashMap<>(); 30 | /** 所有接收到的地标 */ 31 | private static final LinkedHashMap WARPS = new LinkedHashMap<>(); 32 | /** 所有接收到的家计数器 */ 33 | private static int HOMES_COUNTER = 0; 34 | /** 所有接收到的地标计数器 */ 35 | private static int WARPS_COUNTER = 0; 36 | 37 | /** 38 | * 接收家数据 39 | * 40 | * @param player 通道玩家 41 | * @param home 家信息 42 | */ 43 | public static synchronized void receiveHome(ProxiedPlayer player, TransHome home) { 44 | if (home == null) { 45 | Main.send(player, Channel.TransHome.sendC(HOMES_COUNTER)); 46 | HOMES_COUNTER = 0; 47 | 48 | HOMES.forEach((uuid, homes) -> // 49 | homes.forEach((name, loc) -> // 50 | Core.setHome(uuid, name, loc))); 51 | HOMES.clear(); 52 | } else { 53 | HOMES_COUNTER++; 54 | HOMES.computeIfAbsent(home.getPlayer(), HOMES_MF).put(home.getName(), setServer(player, home.getLoc())); 55 | } 56 | } 57 | 58 | /** 59 | * 接收地标数据 60 | * 61 | * @param player 通道玩家 62 | * @param warp 地标信息 63 | */ 64 | public static synchronized void receiveWarp(ProxiedPlayer player, TransWarp warp) { 65 | if (warp == null) { 66 | Main.send(player, Channel.TransWarp.sendC(WARPS_COUNTER)); 67 | WARPS_COUNTER = 0; 68 | 69 | WARPS.forEach(Core::setWarp); 70 | WARPS.clear(); 71 | } else { 72 | WARPS_COUNTER++; 73 | WARPS.put(warp.getName(), setServer(player, warp.getLoc())); 74 | } 75 | } 76 | 77 | /** 78 | * 设置坐标所在的服务器 79 | * 80 | * @param player 玩家 81 | * @param loc 坐标 82 | * 83 | * @return 传入的坐标 84 | */ 85 | private static ShareLocation setServer(ProxiedPlayer player, ShareLocation loc) { 86 | loc.setServer(player.getServer().getInfo().getName()); 87 | return loc; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/src/main/java/yuan/plugins/serverDo/bungee/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月11日 4 | * file: package-info.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | *

8 | * BC端代码 9 | * 10 | * @author yuanlu 11 | *

12 | * BC端代码 13 | * @author yuanlu 14 | */ 15 | /** 16 | * BC端代码 17 | * 18 | * @author yuanlu 19 | * 20 | */ 21 | package yuan.plugins.serverDo.bungee; 22 | -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/src/main/resources/bungee.yml: -------------------------------------------------------------------------------- 1 | name: yuanluServerDo 2 | main: yuan.plugins.serverDo.bungee.Main 3 | version: ${parent.version} 4 | description: ${parent.description} 5 | author: yuanlu 6 | website: ${parent.url} -------------------------------------------------------------------------------- /yuanluServerDo-bungeecord/src/main/resources/proxy-config.yml: -------------------------------------------------------------------------------- 1 | #跨服传送组 2 | server-group: 3 | #同一组内可以使用/tpa, /tpahere等命令 4 | group1: 5 | - server1 6 | - server2 7 | group2: 8 | - server3 9 | - server4 10 | #禁用的服务器, 例如登录服务器 11 | server-ban: 12 | - 'auth' 13 | - 'lobby' 14 | 15 | #名称获取设置 16 | setting: 17 | #如果设置为true,在允许的情况下,从子服获取玩家名称,否则全由bungeecord获取玩家名称 18 | sub-server-display-name: false 19 | 20 | #玩家tab替换内容,可任意修改 21 | player-tab-replace: "%%%yuanlu-tab-player-list" 22 | 23 | #时间校准循环间隔(ms) 24 | timeAmend: 300000 25 | 26 | #自动保存延时(ms) 27 | save-delay: 60000 28 | 29 | #是否启用at 30 | use-at: true 31 | -------------------------------------------------------------------------------- /yuanluServerDo-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bid.yuanlu 9 | yuanluServerDo 10 | 1.2.2 11 | 12 | 13 | yuanluServerDo-common 14 | jar 15 | 16 | YuanluServerDo Common 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-shade-plugin 27 | 28 | 29 | 30 | 31 | src/main/resources 32 | true 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /yuanluServerDo-common/src/main/java/yuan/plugins/serverDo/At.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.AllArgsConstructor; 5 | import lombok.NonNull; 6 | import lombok.experimental.FieldDefaults; 7 | import lombok.val; 8 | 9 | import java.util.Arrays; 10 | import java.util.function.Predicate; 11 | import java.util.stream.Stream; 12 | 13 | /** 14 | * At的帮助类, 用于分析msg中的at字段 15 | * 16 | * @author yuanlu 17 | */ 18 | @FieldDefaults(level = AccessLevel.PRIVATE) 19 | @AllArgsConstructor 20 | public class At { 21 | /** At字符 */ 22 | public static final char AT_CHAR = '@'; 23 | /** At字符 */ 24 | public static final String AT_STR = String.valueOf(AT_CHAR); 25 | /** At颜色 */ 26 | public static final String AT_COLOR = ChatColor.AQUA.toString(); 27 | /** 当前字段的完整字符 */ 28 | String str; 29 | /** 当前字段的首字段(假设为name) */ 30 | String first; 31 | /** 当前字段的颜色 */ 32 | String color; 33 | 34 | /** 35 | * 分析一个msg 36 | * 37 | * @param msg 分段的msg 38 | */ 39 | private At(String msg) { 40 | this(msg, first(msg), ChatColor.getLastColors(msg)); 41 | } 42 | 43 | /** 44 | * 获取msg中所有at的玩家 45 | * 46 | * @param msg 实际的聊天信息 47 | * @param names 玩家名判断 48 | * 49 | * @return at玩家的流 50 | */ 51 | public static Stream at(@NonNull String msg, @NonNull Predicate names) { 52 | if (msg.indexOf(AT_CHAR) < 0) return Stream.empty(); 53 | return Arrays.stream(msg.split(AT_STR, 0))// 54 | .skip(1)// 55 | .map(At::first)// 56 | .filter(name -> names.test(name)); 57 | } 58 | 59 | /** 60 | * 获取首字段 61 | * 62 | * @param msg msg 63 | * 64 | * @return 首字段 65 | */ 66 | private static String first(String msg) { 67 | return msg.split(" ", 2)[0]; 68 | } 69 | 70 | /** 71 | * 处理msg 72 | * 73 | * @param format 由format给定msg的颜色 74 | * @param msg 实际的聊天信息 75 | * @param names 玩家名判断 76 | * 77 | * @return 处理后的实际聊天信息 78 | */ 79 | public static String format(String format, @NonNull String msg, @NonNull Predicate names) { 80 | if (msg.indexOf(AT_CHAR) < 0) return msg; 81 | 82 | format = format == null ? "" : getFormatColor(format); 83 | if (format.isEmpty()) format = ChatColor.RESET.toString(); 84 | 85 | val ats = Arrays.stream((format + msg).split(AT_STR, -1))// 按At分割 86 | .map(At::new)// 87 | .toArray(At[]::new); 88 | for (int i = 1; i < ats.length; i++) ats[i].previous(ats[i - 1]); 89 | 90 | val msgNew = new StringBuilder(); 91 | Arrays.stream(ats)// 92 | .map(at -> at.toString(names))// 93 | .forEach(msgNew::append); 94 | return msgNew.substring(msgNew.indexOf(AT_STR) + 1 + format.length()); 95 | } 96 | 97 | /** 98 | * 获取聊天格式的msg颜色 99 | * 100 | * @param format 聊天格式 101 | * 102 | * @return msg的颜色 103 | */ 104 | private static String getFormatColor(String format) { 105 | format = String.format(format, "", "\0"); 106 | return ChatColor.getLastColors(format.substring(0, format.indexOf('\0'))); 107 | } 108 | 109 | /** 110 | * 由前一个at字段的颜色传播至此at字段 111 | * 112 | * @param at 前一个at字段 113 | */ 114 | private void previous(At at) { 115 | color = ChatColor.getLastColors(at.color + color); 116 | } 117 | 118 | @Override 119 | public String toString() { 120 | return String.format("At [str=%s, first=%s, color=%s]", str, first, color.replace(ChatColor.COLOR_CHAR, '&')); 121 | } 122 | 123 | /** 124 | * 检测名字并转为at段 125 | * 126 | * @param names 所有玩家名 127 | * 128 | * @return 字符串 129 | */ 130 | private String toString(Predicate names) { 131 | if (names.test(first)) { 132 | return String.format("%s%c%s%s%s", // 133 | AT_COLOR, // 134 | AT_CHAR, // 135 | first, // 136 | ChatColor.getLastColors(ChatColor.COLOR_CHAR + "r" + color), // 137 | str.substring(first.length())// 138 | ); 139 | } else { 140 | return AT_STR + str; 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /yuanluServerDo-common/src/main/java/yuan/plugins/serverDo/ChatColor.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo; 2 | 3 | import lombok.NonNull; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * All supported color values for chat 10 | */ 11 | @SuppressWarnings("javadoc") 12 | public enum ChatColor { 13 | /** 14 | * Represents black 15 | */ 16 | BLACK('0'), 17 | /** 18 | * Represents dark blue 19 | */ 20 | DARK_BLUE('1'), 21 | /** 22 | * Represents dark green 23 | */ 24 | DARK_GREEN('2'), 25 | /** 26 | * Represents dark blue (aqua) 27 | */ 28 | DARK_AQUA('3'), 29 | /** 30 | * Represents dark red 31 | */ 32 | DARK_RED('4'), 33 | /** 34 | * Represents dark purple 35 | */ 36 | DARK_PURPLE('5'), 37 | /** 38 | * Represents gold 39 | */ 40 | GOLD('6'), 41 | /** 42 | * Represents gray 43 | */ 44 | GRAY('7'), 45 | /** 46 | * Represents dark gray 47 | */ 48 | DARK_GRAY('8'), 49 | /** 50 | * Represents blue 51 | */ 52 | BLUE('9'), 53 | /** 54 | * Represents green 55 | */ 56 | GREEN('a'), 57 | /** 58 | * Represents aqua 59 | */ 60 | AQUA('b'), 61 | /** 62 | * Represents red 63 | */ 64 | RED('c'), 65 | /** 66 | * Represents light purple 67 | */ 68 | LIGHT_PURPLE('d'), 69 | /** 70 | * Represents yellow 71 | */ 72 | YELLOW('e'), 73 | /** 74 | * Represents white 75 | */ 76 | WHITE('f'), 77 | /** 78 | * Represents magical characters that change around randomly 79 | */ 80 | MAGIC('k', true), 81 | /** 82 | * Makes the text bold. 83 | */ 84 | BOLD('l', true), 85 | /** 86 | * Makes a line appear through the text. 87 | */ 88 | STRIKETHROUGH('m', true), 89 | /** 90 | * Makes the text appear underlined. 91 | */ 92 | UNDERLINE('n', true), 93 | /** 94 | * Makes the text italic. 95 | */ 96 | ITALIC('o', true), 97 | /** 98 | * Resets all previous chat colors or formats. 99 | */ 100 | RESET('r'); 101 | 102 | /** 103 | * The special character which prefixes all chat colour codes. Use this if you 104 | * need to dynamically convert colour codes from your custom format. 105 | */ 106 | public static final char COLOR_CHAR = '\u00A7'; 107 | private static final Map BY_CHAR = new HashMap<>(); 108 | 109 | static { 110 | for (ChatColor color : values()) { 111 | BY_CHAR.put(color.code, color); 112 | } 113 | } 114 | 115 | private final char code; 116 | private final boolean isFormat; 117 | private final String toString; 118 | 119 | private ChatColor(char code) { 120 | this(code, false); 121 | } 122 | 123 | private ChatColor(char code, boolean isFormat) { 124 | this.code = code; 125 | this.isFormat = isFormat; 126 | this.toString = new String(new char[] { COLOR_CHAR, code }); 127 | } 128 | 129 | /** 130 | * Gets the color represented by the specified color code 131 | * 132 | * @param code Code to check 133 | * 134 | * @return Associative {@link ChatColor} with the given code, or null 135 | * if it doesn't exist 136 | */ 137 | public static ChatColor getByChar(char code) { 138 | return BY_CHAR.get(code); 139 | } 140 | 141 | /** 142 | * Gets the ChatColors used at the end of the given input string. 143 | * 144 | * @param input Input string to retrieve the colors from. 145 | * 146 | * @return Any remaining ChatColors to pass onto the next line. 147 | */ 148 | public static String getLastColors(@NonNull String input) { 149 | 150 | String result = ""; 151 | int length = input.length(); 152 | 153 | // Search backwards from the end as it is faster 154 | for (int index = length - 1; index > -1; index--) { 155 | char section = input.charAt(index); 156 | if (section == COLOR_CHAR && index < length - 1) { 157 | char c = input.charAt(index + 1); 158 | ChatColor color = getByChar(c); 159 | 160 | if (color != null) { 161 | result = color.toString() + result; 162 | 163 | // Once we find a color or reset we can stop searching 164 | if (color.isColor() || color.equals(RESET)) { 165 | break; 166 | } 167 | } 168 | } 169 | } 170 | 171 | return result; 172 | } 173 | 174 | /** 175 | * Checks if this code is a color code as opposed to a format code. 176 | * 177 | * @return whether this ChatColor is a color code 178 | */ 179 | public boolean isColor() { 180 | return !isFormat && this != RESET; 181 | } 182 | 183 | @Override 184 | public String toString() { 185 | return toString; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /yuanluServerDo-common/src/main/java/yuan/plugins/serverDo/LRUCache.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.val; 5 | 6 | import java.util.Arrays; 7 | import java.util.Objects; 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | /** 11 | * 最近最少使用 12 | * 13 | * @param K 14 | * @param V 15 | * 16 | * @author sun.misc 17 | */ 18 | @SuppressWarnings("unchecked") 19 | public abstract class LRUCache { 20 | /** 缓存使用数 */ 21 | public static final AtomicInteger CACHE_USE = new AtomicInteger(); 22 | /** 缓存 */ 23 | private Object[] cache = null; 24 | /** 缓存大小 */ 25 | private int size; 26 | 27 | /** @param size 缓存大小 */ 28 | public LRUCache(int size) { 29 | this.size = size; 30 | } 31 | 32 | /** 33 | * 将元素移到最前端 34 | * 35 | * @param objs 数据 36 | * @param index 要移动的数据 37 | */ 38 | private static void moveToFront(Object[] objs, int index) { 39 | Object obj = objs[index]; 40 | for (int i = index; i > 0; i--) { 41 | objs[i] = objs[(i - 1)]; 42 | } 43 | objs[0] = obj; 44 | } 45 | 46 | /** 47 | * 检出元素, 不会对缓存进行任何修改 48 | * 49 | * @param k 键 50 | * 51 | * @return 值, 当不存在时返回null 52 | */ 53 | public synchronized final V check(K k) { 54 | CACHE_USE.addAndGet(1); 55 | if (cache != null) { 56 | for (int i = 0; i < size; i++) { 57 | Node now = (Node) cache[i]; 58 | if (now == null) break; 59 | if (Objects.equals(k, now.k)) return now.v; 60 | } 61 | } 62 | return null; 63 | } 64 | 65 | /** 66 | * 清空缓存 67 | * 68 | * @return 是否有元素被清除 69 | */ 70 | public synchronized final boolean clearCache() { 71 | if (cache == null) return false; 72 | int i; 73 | for (i = 0; i < size; i++) { 74 | Node node = (Node) cache[i]; 75 | if (node == null) break; 76 | clearHandle(node.k, node.v); 77 | } 78 | Arrays.fill(cache, 0, i, null); 79 | return i != 0; 80 | } 81 | 82 | /** 83 | * 清除处理
84 | * 当一个缓存被清除时触发 85 | * 86 | * @param k 键 87 | * @param v 值 88 | */ 89 | protected void clearHandle(K k, V v) { 90 | 91 | } 92 | 93 | /** 94 | * 创建对象 95 | * 96 | * @param k 键 97 | * 98 | * @return 值 99 | */ 100 | protected abstract V create(K k); 101 | 102 | /** 103 | * 获取元素 104 | * 105 | * @param k 键 106 | * 107 | * @return 值 108 | */ 109 | public synchronized final V get(K k) { 110 | CACHE_USE.addAndGet(1); 111 | if (cache == null) cache = new Object[size]; 112 | else { 113 | for (int i = 0; i < size; i++) { 114 | Node now = (Node) cache[i]; 115 | if (now == null) break; 116 | if (Objects.equals(k, now.k)) { 117 | if (i > 0) moveToFront(cache, i); 118 | return now.v; 119 | } 120 | } 121 | } 122 | V v = create(k); 123 | Node node = (Node) cache[size - 1]; 124 | if (node != null) { 125 | clearHandle(node.k, node.v); 126 | node.set(k, v); 127 | } else cache[size - 1] = new Node<>(k, v); 128 | moveToFront(cache, size - 1); 129 | return v; 130 | } 131 | 132 | /** 133 | * 重新调整缓冲区大小 134 | * 135 | * @param size 大小 136 | */ 137 | public synchronized final void resize(int size) { 138 | if (cache != null) { 139 | if (cache.length < size || cache.length > size * 2) { 140 | val old = cache; 141 | cache = new Node[size]; 142 | System.arraycopy(old, 0, cache, 0, Math.min(old.length, size)); 143 | } else Arrays.fill(cache, size, cache.length, null); 144 | } 145 | this.size = size; 146 | } 147 | 148 | /** 149 | * 节点 150 | * 151 | * @param 键 152 | * @param 值 153 | * 154 | * @author yuanlu 155 | */ 156 | @AllArgsConstructor 157 | @SuppressWarnings("javadoc") 158 | private static final class Node { 159 | K k; 160 | V v; 161 | 162 | void set(K k, V v) { 163 | this.k = k; 164 | this.v = v; 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /yuanluServerDo-common/src/main/java/yuan/plugins/serverDo/ShareData.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月9日 4 | * file: ShareData.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo; 9 | 10 | import lombok.*; 11 | 12 | import java.nio.charset.Charset; 13 | import java.util.Arrays; 14 | import java.util.HashMap; 15 | import java.util.logging.Logger; 16 | 17 | /** 18 | * 共享数据 19 | * 20 | * @author yuanlu 21 | */ 22 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 23 | public final class ShareData { 24 | /** 插件名称 用于信息提示 模板自动生成 */ 25 | public final static String SHOW_NAME = "元路跨服操作插件"; 26 | /** 配置文件 */ 27 | public static final Charset CHARSET = Charset.forName("UTF-8"); 28 | /** BC通道名 */ 29 | public static final String BC_CHANNEL = "bc:yuanlu-SDo".toLowerCase(); 30 | /** 日志记录器 */ 31 | private static @Setter 32 | @Getter Logger logger; 33 | /** 是否开启DEBUG模式 */ 34 | private static @Setter 35 | @Getter boolean DEBUG = false; 36 | 37 | /** 38 | * 读取一个int 39 | * 40 | * @param bs byte[] 41 | * @param offset 偏移下标 42 | * @param def 默认值 43 | * 44 | * @return 读取的int
45 | * null: 超出长度 46 | */ 47 | public static int readInt(byte[] bs, int offset, int def) { 48 | if (bs.length < offset + 4) return def; 49 | return (bs[offset++] << 24) + (bs[offset++] << 16) + (bs[offset++] << 8) + (bs[offset++] << 0); 50 | } 51 | 52 | /** 53 | * Tab补全类型 54 | * 55 | * @author yuanlu 56 | */ 57 | @SuppressWarnings("javadoc") 58 | public enum TabType { 59 | TP_ALL, TP_NORMAL, WARP, HOME, AT; 60 | 61 | private static final int length; 62 | private static final HashMap DATAS = new HashMap<>(); 63 | 64 | static { 65 | val vs = values(); 66 | length = Integer.toString(vs.length - 1, Character.MAX_VALUE).length(); 67 | char[] emptyChar = new char[length]; 68 | Arrays.fill(emptyChar, '_'); 69 | String empty = new String(emptyChar); 70 | for (val v : vs) { 71 | v.key = (Integer.toString(v.ordinal(), Character.MAX_VALUE) + empty).substring(0, length); 72 | DATAS.put(v.key, v); 73 | } 74 | } 75 | 76 | private String key; 77 | 78 | public static TabType getType(@NonNull String tab, @NonNull String arg) { 79 | if (!arg.startsWith(tab)) return null; 80 | val tlen = tab.length(); 81 | if (arg.length() < tlen + length) return null; 82 | arg = arg.substring(tlen, tlen + length); 83 | return DATAS.get(arg); 84 | } 85 | 86 | public static String getValue(@NonNull String tab, @NonNull String arg) { 87 | if (!arg.startsWith(tab)) return null; 88 | val tlen = tab.length() + length; 89 | if (arg.length() < tlen) return null; 90 | return arg.substring(tlen).toLowerCase(); 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return key; 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /yuanluServerDo-common/src/main/java/yuan/plugins/serverDo/ShareLocation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo; 5 | 6 | import lombok.*; 7 | 8 | /** 9 | * 共享用的坐标 10 | * 11 | * @author yuanlu 12 | */ 13 | @RequiredArgsConstructor 14 | @AllArgsConstructor 15 | @ToString 16 | @SuppressWarnings("javadoc") 17 | @EqualsAndHashCode 18 | public final class ShareLocation { 19 | private final @Getter double x, y, z; 20 | private final @Getter float yaw, pitch; 21 | private final @Getter String world; 22 | 23 | private @Setter 24 | @Getter String server; 25 | 26 | @Override 27 | public ShareLocation clone() { 28 | return new ShareLocation(x, y, z, yaw, pitch, world, server); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /yuanluServerDo-common/src/main/java/yuan/plugins/serverDo/WaitMaintain.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo; 2 | 3 | import lombok.*; 4 | 5 | import java.util.Collection; 6 | import java.util.Map; 7 | import java.util.concurrent.DelayQueue; 8 | import java.util.concurrent.Delayed; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.function.Supplier; 11 | 12 | /** 13 | * 等待任务的维护清理 14 | * 15 | * @author yuanlu 16 | */ 17 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 18 | public final class WaitMaintain { 19 | /** 队列 */ 20 | private static final DelayQueue QUEUE = new DelayQueue<>(); 21 | /** 等待时长(ms) 网络 */ 22 | public static @Getter 23 | @Setter long T_Net = 5 * 1000; 24 | /** 等待时长(ms) 用户 */ 25 | public static @Getter 26 | @Setter long T_User = 120 * 1000; 27 | 28 | static { 29 | new Thread("YSD-" + WaitMaintain.class) { 30 | @Override 31 | public void run() { 32 | while (true) { 33 | try { 34 | val ele = QUEUE.take(); 35 | if (ele != null) ele.handle(); 36 | } catch (Throwable e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | } 41 | }.start(); 42 | } 43 | 44 | /** 45 | * 将键放入set,并设置最长超时时间, 超时后将被清理 46 | * 47 | * @param 数据类型 48 | * @param set 图 49 | * @param k 键 50 | * @param maxTime 等待时长 51 | * 52 | * @return return 53 | */ 54 | public static boolean add(Collection set, K k, long maxTime) { 55 | return add(set, k, maxTime, null); 56 | } 57 | 58 | /** 59 | * 将键放入set,并设置最长超时时间, 超时后将被清理 60 | * 61 | * @param 数据类型 62 | * @param set 图 63 | * @param k 键 64 | * @param maxTime 等待时长 65 | * @param clearListener 清理监听 66 | * 67 | * @return return 68 | */ 69 | public static boolean add(Collection set, K k, long maxTime, Runnable clearListener) { 70 | val r = set.add(k); 71 | QUEUE.add(new CElement(System.currentTimeMillis() + maxTime, set, k, clearListener)); 72 | return r; 73 | } 74 | 75 | /** 76 | * 将一对多键值对放入map,并设置最长超时时间, 超时后将被清理 77 | * 78 | * @param 数据类型 79 | * @param 数据类型 80 | * @param 多值列表类型 81 | * @param map 图 82 | * @param k 键 83 | * @param v 值 84 | * @param maxTime 等待时长 85 | * @param builder 列表构造器 86 | * @param clearListener 清理监听 87 | * 88 | * @return return 89 | */ 90 | public static > boolean add(Map map, K k, V v, long maxTime, Supplier builder, Runnable clearListener) { 91 | L c = map.get(k); 92 | if (c == null) map.put(k, c = builder.get()); 93 | val r = c.add(v); 94 | QUEUE.add(new MutiMapElement(System.currentTimeMillis() + maxTime, map, k, v, clearListener)); 95 | return r; 96 | } 97 | 98 | /** 99 | * 监听数值, 并设置最长超时时间, 若超时后数值未发生改变则触发清理 100 | * 101 | * @param 数据类型 102 | * @param num 数值 103 | * @param maxTime 等待时长 104 | * @param clearListener 清理监听 105 | */ 106 | public static void monitor(@NonNull Number num, long maxTime, @NonNull Runnable clearListener) { 107 | QUEUE.add(new NElement(System.currentTimeMillis() + maxTime, clearListener, num)); 108 | } 109 | 110 | /** 111 | * 将键值对放入map,并设置最长超时时间, 超时后将被清理 112 | * 113 | * @param 数据类型 114 | * @param 数据类型 115 | * @param map 图 116 | * @param k 键 117 | * @param v 值 118 | * @param maxTime 等待时长 119 | * 120 | * @return return 121 | */ 122 | public static V put(Map map, K k, V v, long maxTime) { 123 | return put(map, k, v, maxTime, null); 124 | } 125 | 126 | /** 127 | * 将键值对放入map,并设置最长超时时间, 超时后将被清理 128 | * 129 | * @param 数据类型 130 | * @param 数据类型 131 | * @param map 图 132 | * @param k 键 133 | * @param v 值 134 | * @param maxTime 等待时长 135 | * @param clearListener 清理监听 136 | * 137 | * @return return 138 | */ 139 | public static V put(Map map, K k, V v, long maxTime, Runnable clearListener) { 140 | val old = map.put(k, v); 141 | QUEUE.add(new MapElement(System.currentTimeMillis() + maxTime, map, k, v, clearListener)); 142 | return old; 143 | } 144 | 145 | /** 146 | * 将一对多键值对放入map,并设置最长超时时间, 超时后将被清理 147 | * 148 | * @param 数据类型 149 | * @param 数据类型 150 | * @param 数据类型 151 | * @param 值Map类型 152 | * @param map 图 153 | * @param t 键1 154 | * @param k 键2 155 | * @param v 值 156 | * @param maxTime 等待时长 157 | * @param builder 列表构造器 158 | * @param clearListener 清理监听 159 | * 160 | * @return return 161 | */ 162 | public static > V put(Map map, T t, K k, V v, long maxTime, Supplier builder, Runnable clearListener) { 163 | M m = map.get(t); 164 | if (m == null) map.put(t, m = builder.get()); 165 | val r = m.put(k, v); 166 | QUEUE.add(new MapMapElement(System.currentTimeMillis() + maxTime, map, t, k, v, clearListener)); 167 | return r; 168 | } 169 | 170 | /** 171 | * 延时监听元素 172 | * 173 | * @author yuanlu 174 | */ 175 | @Value 176 | @EqualsAndHashCode(callSuper = true) 177 | @SuppressWarnings("rawtypes") 178 | private static final class CElement extends Element { 179 | 180 | /** 图 */ 181 | Collection set; 182 | 183 | /** 键 */ 184 | Object k; 185 | 186 | @SuppressWarnings("javadoc") 187 | public CElement(long expire, Collection set, Object k, Runnable clearListener) { 188 | super(expire, clearListener); 189 | this.set = set; 190 | this.k = k; 191 | } 192 | 193 | /** 处理 */ 194 | @Override 195 | void handle() { 196 | if (set.remove(k) && clearListener != null) clearListener.run(); 197 | } 198 | } 199 | 200 | /** 201 | * 延时监听元素 202 | * 203 | * @author yuanlu 204 | */ 205 | @AllArgsConstructor 206 | private static abstract class Element implements Delayed { 207 | /** 单位 */ 208 | private static final TimeUnit U = TimeUnit.MILLISECONDS; 209 | 210 | /** 到期时间 */ 211 | long expire; 212 | /** 清理监听 */ 213 | Runnable clearListener; 214 | 215 | @Override 216 | public int compareTo(Delayed o) { 217 | return Long.compare(getDelay(U), o.getDelay(U)); 218 | } 219 | 220 | @Override 221 | public long getDelay(TimeUnit unit) { 222 | return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS); 223 | } 224 | 225 | /** 处理 */ 226 | abstract void handle(); 227 | 228 | } 229 | 230 | /** 231 | * 延时监听元素 232 | * 233 | * @author yuanlu 234 | */ 235 | @Value 236 | @EqualsAndHashCode(callSuper = true) 237 | @SuppressWarnings("rawtypes") 238 | private static final class MapElement extends Element { 239 | 240 | /** 图 */ 241 | Map map; 242 | 243 | /** 键 */ 244 | Object k; 245 | 246 | /** 值 */ 247 | Object old; 248 | 249 | @SuppressWarnings("javadoc") 250 | public MapElement(long expire, Map map, Object k, Object old, Runnable clearListener) { 251 | super(expire, clearListener); 252 | this.map = map; 253 | this.k = k; 254 | this.old = old; 255 | } 256 | 257 | /** 处理 */ 258 | @Override 259 | void handle() { 260 | if (map.remove(k, old) && clearListener != null) clearListener.run(); 261 | } 262 | } 263 | 264 | /** 265 | * 延时监听元素 266 | * 267 | * @author yuanlu 268 | */ 269 | @Value 270 | @EqualsAndHashCode(callSuper = true) 271 | @SuppressWarnings("rawtypes") 272 | private static final class MapMapElement extends Element { 273 | 274 | /** 图 */ 275 | Map map; 276 | 277 | /** 主键 */ 278 | Object t; 279 | /** 副键 */ 280 | Object k; 281 | 282 | /** 值 */ 283 | Object v; 284 | 285 | @SuppressWarnings("javadoc") 286 | public MapMapElement(long expire, Map map, Object t, Object k, Object v, Runnable clearListener) { 287 | super(expire, clearListener); 288 | this.map = map; 289 | this.t = t; 290 | this.k = k; 291 | this.v = v; 292 | } 293 | 294 | /** 处理 */ 295 | @Override 296 | void handle() { 297 | Map m = (Map) map.get(t); 298 | if (m == null) return; 299 | boolean clear = m.remove(k, v); 300 | if (m.isEmpty()) map.remove(t, m); 301 | if (clear && clearListener != null) clearListener.run(); 302 | } 303 | } 304 | 305 | /** 306 | * 延时监听元素 307 | * 308 | * @author yuanlu 309 | */ 310 | @Value 311 | @EqualsAndHashCode(callSuper = true) 312 | @SuppressWarnings("rawtypes") 313 | private static final class MutiMapElement extends Element { 314 | 315 | /** 图 */ 316 | Map map; 317 | 318 | /** 键 */ 319 | Object k; 320 | 321 | /** 值 */ 322 | Object old; 323 | 324 | @SuppressWarnings("javadoc") 325 | public MutiMapElement(long expire, Map map, Object k, Object old, Runnable clearListener) { 326 | super(expire, clearListener); 327 | this.map = map; 328 | this.k = k; 329 | this.old = old; 330 | } 331 | 332 | /** 处理 */ 333 | @Override 334 | void handle() { 335 | Collection c = (Collection) map.get(k); 336 | if (c == null) return; 337 | boolean clear = c.remove(old); 338 | if (c.isEmpty()) map.remove(k, c); 339 | if (clear && clearListener != null) clearListener.run(); 340 | } 341 | } 342 | 343 | /** 344 | * 延时监听元素 345 | * 346 | * @author yuanlu 347 | */ 348 | @Value 349 | @EqualsAndHashCode(callSuper = true) 350 | private static final class NElement extends Element { 351 | /** 原始值 */ 352 | long originalLong; 353 | /** 原始值 */ 354 | double originalDouble; 355 | /** 更新值 */ 356 | Number num; 357 | 358 | @SuppressWarnings("javadoc") 359 | public NElement(long expire, Runnable clearListener, Number num) { 360 | super(expire, clearListener); 361 | this.num = num; 362 | this.originalDouble = num.doubleValue(); 363 | this.originalLong = num.longValue(); 364 | } 365 | 366 | @Override 367 | void handle() { 368 | if (num.doubleValue() == originalDouble && // 369 | num.longValue() == originalLong) clearListener.run(); 370 | } 371 | 372 | } 373 | 374 | } 375 | -------------------------------------------------------------------------------- /yuanluServerDo-common/src/main/java/yuan/plugins/serverDo/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月9日 4 | * file: package-info.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | *

8 | * yuanluServerDo 9 | * 10 | * @author yuanlu 11 | *

12 | * yuanluServerDo 13 | * @author yuanlu 14 | */ 15 | /** 16 | * yuanluServerDo 17 | * 18 | * @author yuanlu 19 | * 20 | */ 21 | package yuan.plugins.serverDo; 22 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bid.yuanlu 9 | yuanluServerDo 10 | 1.2.2 11 | 12 | 13 | yuanluServerDo-velocity 14 | jar 15 | 16 | YuanluServerDo Velocity 17 | 18 | 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 24 | 25 | org.apache.maven.plugins 26 | maven-shade-plugin 27 | 28 | 29 | 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | src/main/resources 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | papermc 47 | https://repo.papermc.io/repository/maven-public/ 48 | 49 | 50 | Sonatype 51 | https://s01.oss.sonatype.org/content/repositories/snapshots/ 52 | 53 | 54 | 55 | 56 | 57 | com.velocitypowered 58 | velocity-api 59 | 3.3.0-SNAPSHOT 60 | provided 61 | 62 | 63 | bid.yuanlu 64 | yuanluServerDo-common 65 | ${project.version} 66 | compile 67 | 68 | 69 | 70 | net.md-5 71 | bungeecord-config 72 | 1.16-R0.4 73 | compile 74 | 75 | 76 | 77 | org.bstats 78 | bstats-velocity 79 | 2.2.1 80 | compile 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/main/java/yuan/plugins/serverDo/velocity/CmdProxy.java: -------------------------------------------------------------------------------- 1 | package yuan.plugins.serverDo.velocity; 2 | 3 | import com.velocitypowered.api.command.RawCommand; 4 | import com.velocitypowered.api.proxy.Player; 5 | import com.velocitypowered.api.proxy.ProxyServer; 6 | import lombok.NonNull; 7 | import lombok.val; 8 | import yuan.plugins.serverDo.Channel; 9 | 10 | import java.util.*; 11 | import java.util.concurrent.CompletableFuture; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.atomic.AtomicLong; 14 | 15 | public class CmdProxy { 16 | private static final Set PROXYED_CMDS = ConcurrentHashMap.newKeySet(); 17 | ProxyServer proxy; 18 | 19 | public static boolean isProxyed(String cmd) { 20 | return PROXYED_CMDS.contains(cmd); 21 | } 22 | 23 | public static void addCmds(@NonNull String namespace, @NonNull Collection cmds) { 24 | ArrayList newCmds = new ArrayList<>(0); 25 | synchronized (PROXYED_CMDS) { 26 | for (String cmd : cmds) { 27 | if (PROXYED_CMDS.add(cmd)) newCmds.add(cmd); 28 | val ncmd = namespace + ":" + cmd; 29 | if (PROXYED_CMDS.add(ncmd)) newCmds.add(ncmd); 30 | } 31 | } 32 | if (newCmds.isEmpty()) return; 33 | val fst = newCmds.remove(newCmds.size() - 1); 34 | Main.getMain().getProxy().getCommandManager().register(fst, new SuggestCommand(), newCmds.toArray(new String[0])); 35 | } 36 | 37 | public static void cmdTabCallback(long id, Player player, @NonNull ArrayList tabs) { 38 | TabHandler.TabComplete(player, tabs); 39 | val future = SuggestCommand.FUTURE_MAP.remove(id); 40 | if (future == null) return; 41 | future.complete(tabs); 42 | } 43 | 44 | private static class SuggestCommand implements RawCommand { 45 | private static final Map>> FUTURE_MAP = new ConcurrentHashMap<>(); 46 | private static final AtomicLong counter = new AtomicLong(0); 47 | 48 | @Override 49 | public void execute(Invocation invocation) { 50 | throw new InternalError("Bad command call, logic error!!"); 51 | } 52 | 53 | @Override 54 | public CompletableFuture> suggestAsync(Invocation invocation) { 55 | CompletableFuture> future = new CompletableFuture<>(); 56 | if (invocation.source() instanceof Player) { 57 | val p = (Player) invocation.source(); 58 | val id = counter.incrementAndGet(); 59 | Main.send(p, Channel.TabParse.sendS(id, invocation.alias() + " " + invocation.arguments())); 60 | FUTURE_MAP.put(id, future); 61 | } else { 62 | future.complete(new ArrayList<>(0)); 63 | } 64 | return future; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/main/java/yuan/plugins/serverDo/velocity/ConfigManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.velocity; 5 | 6 | import com.google.common.base.Objects; 7 | import com.velocitypowered.api.proxy.Player; 8 | import com.velocitypowered.api.proxy.ServerConnection; 9 | import lombok.*; 10 | import net.md_5.bungee.config.Configuration; 11 | import net.md_5.bungee.config.ConfigurationProvider; 12 | import net.md_5.bungee.config.YamlConfiguration; 13 | import yuan.plugins.serverDo.*; 14 | import yuan.plugins.serverDo.Tool.ThrowableFunction; 15 | import yuan.plugins.serverDo.Tool.ThrowableRunnable; 16 | 17 | import java.io.*; 18 | import java.util.*; 19 | import java.util.concurrent.ConcurrentHashMap; 20 | 21 | /** 22 | * 配置管理器 23 | * 24 | * @author yuanlu 25 | */ 26 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 27 | public final class ConfigManager { 28 | /** 地标点 */ 29 | static final HashMap WARPS = new LinkedHashMap<>(); 30 | /** 家点 */ 31 | static final HomesLRU HOMES = new HomesLRU(32); 32 | /** 服务器组 */ 33 | private static final HashMap> GROUPS = new HashMap<>(); 34 | /** 禁用的服务器 */ 35 | private static final HashSet BAN_SERVER = new HashSet<>(); 36 | /** 配置文件 */ 37 | private static @Getter Configuration config; 38 | /** tab替换 */ 39 | private static @Getter 40 | @Setter String tabReplace; 41 | /** 服务器信息 */ 42 | private static byte[] serverInfo; 43 | /** 出错 */ 44 | private static @Getter boolean errorGroup; 45 | /** 自动保存延时 */ 46 | private @Getter 47 | @Setter 48 | static long saveDelay = 1000 * 60; 49 | /** 是否使用AT功能 */ 50 | private @Getter 51 | @Setter 52 | static boolean useAt = true; 53 | 54 | /** 55 | * 检测是否启用服务器 56 | * 57 | * @param server 服务器 58 | * 59 | * @return 此服务器是否启用本插件 60 | */ 61 | public static boolean allowServer(String server) { 62 | return !BAN_SERVER.contains(server); 63 | } 64 | 65 | /** 66 | * 检测是否可以传送 67 | * 68 | * @param s1 服务器1 69 | * @param s2 服务器2 70 | * 71 | * @return 是否可以传送 72 | */ 73 | public static boolean canTp(String s1, String s2) { 74 | if (BAN_SERVER.contains(s1) || BAN_SERVER.contains(s2)) return false; 75 | if (Objects.equal(s1, s2)) return true; 76 | if (errorGroup) { 77 | if (Main.isDEBUG()) Main.getMain().getLogger().warning("error: 服务器组不存在"); 78 | return false; 79 | } 80 | val group = GROUPS.get(s1); 81 | return group != null && group.contains(s2); 82 | } 83 | 84 | /** 85 | * 关闭时保存 86 | */ 87 | public static void closeSave() { 88 | val list = new ArrayList<>(ConfFile.SAVE_DELAY.keySet()); 89 | ConfFile.SAVE_DELAY.clear(); 90 | list.forEach(ConfFile::save); 91 | for (val v : PlayerConfFile.values()) { 92 | if (v.SAVE_DELAY.isEmpty()) continue; 93 | val l = new ArrayList<>(v.SAVE_DELAY.keySet()); 94 | v.SAVE_DELAY.clear(); 95 | l.forEach(v::save); 96 | } 97 | } 98 | 99 | /** 100 | * 初始化 101 | * 102 | * @param config config 103 | */ 104 | public static void init(Configuration config) { 105 | ConfigManager.config = config; 106 | val tabReplace = config.getString("player-tab-replace", "yl★:" + Tool.randomString(8)); 107 | setTabReplace(tabReplace); 108 | setSaveDelay(config.getLong("save-delay", getSaveDelay())); 109 | setUseAt(config.getBoolean("use-at", isUseAt())); 110 | loadGroup(config); 111 | serverInfo = Channel.ServerInfo.sendS(tabReplace, Main.getPluginContainer().getDescription().getVersion().orElse("Unknown"), 112 | Channel.ServerInfo.ServerPkg.ProxyType.Velocity); 113 | 114 | Arrays.stream(ConfFile.values()).forEach(ConfFile::load); 115 | 116 | } 117 | 118 | /** 119 | * 加载组 120 | * 121 | * @param config 配置文件 122 | */ 123 | private static void loadGroup(Configuration config) { 124 | val sg = config.getSection("server-group"); 125 | if (sg == null) { 126 | errorGroup = true; 127 | Main.getMain().getLogger().warning("[SERVER GROUP] config error!"); 128 | return; 129 | } 130 | for (val key : sg.getKeys()) { 131 | val group = sg.getStringList(key); 132 | for (val server : group) { 133 | HashSet canTp = GROUPS.computeIfAbsent(server, k -> new HashSet<>()); 134 | canTp.addAll(group); 135 | } 136 | if (ShareData.isDEBUG()) ShareData.getLogger().info("加载组 " + key + ": " + group); 137 | } 138 | 139 | BAN_SERVER.clear(); 140 | BAN_SERVER.addAll(config.getStringList("server-ban")); 141 | } 142 | 143 | /** 144 | * 保存配置
145 | * 将会延时保存 146 | * 147 | * @param f 配置类型 148 | */ 149 | public static void saveConf(ConfFile f) { 150 | WaitMaintain.put(ConfFile.SAVE_DELAY, f, System.currentTimeMillis(), saveDelay, f::save); 151 | } 152 | 153 | /** 154 | * 保存配置
155 | * 将会延时保存 156 | * 157 | * @param f 配置类型 158 | * @param player 对应玩家 159 | * 160 | * @see #saveConf(PlayerConfFile, UUID) 161 | */ 162 | public static void saveConf(PlayerConfFile f, Player player) { 163 | saveConf(f, player.getUniqueId()); 164 | } 165 | 166 | /** 167 | * 保存配置
168 | * 将会延时保存 169 | * 170 | * @param f 配置类型 171 | * @param u 对应玩家UUID 172 | */ 173 | public static void saveConf(PlayerConfFile f, UUID u) { 174 | f.needSave.add(u); 175 | WaitMaintain.put(f.SAVE_DELAY, u, System.currentTimeMillis(), saveDelay, () -> f.save(u)); 176 | } 177 | 178 | /** 179 | * 发送BC信息给子服务器 180 | * 181 | * @param server 服务器 182 | */ 183 | public static void sendBungeeInfoToServer(ServerConnection server) { 184 | Main.send(server, serverInfo); 185 | } 186 | 187 | /** 188 | * 配置文件 189 | * 190 | * @author yuanlu 191 | */ 192 | @Getter 193 | @AllArgsConstructor 194 | public enum ConfFile { 195 | /** 自动隐身 */ 196 | ALWAYS_VANISH("alwaysvanish.uid") { 197 | @Override 198 | protected void load0() throws IOException { 199 | try (BufferedReader in = new BufferedReader(new FileReader(getFile()))) { 200 | in.lines().forEach(s -> { 201 | try { 202 | Core.alwaysVanish.add(UUID.fromString(s)); 203 | } catch (IllegalArgumentException e) { 204 | ShareData.getLogger().warning("[Conf] " + fname + ": Bad UUID: " + s); 205 | e.printStackTrace(); 206 | } 207 | }); 208 | } 209 | } 210 | 211 | @Override 212 | protected void save0() throws IOException { 213 | try (BufferedWriter out = new BufferedWriter(new FileWriter(getFile()))) { 214 | for (UUID u : Core.alwaysVanish) { 215 | out.write(u.toString()); 216 | out.write('\n'); 217 | } 218 | } 219 | } 220 | 221 | }, 222 | /** 传送地标 */ 223 | WARP("warp.yml") { 224 | @Override 225 | protected void load0() throws IOException { 226 | Configuration warps = YAML.load(getFile()); 227 | for (String name : warps.getKeys()) { 228 | Configuration warp = warps.getSection(name); 229 | 230 | String world = warp.getString("world", null); 231 | String server = warp.getString("server", null); 232 | double x = warp.getDouble("x", Double.NaN); 233 | double y = warp.getDouble("y", Double.NaN); 234 | double z = warp.getDouble("z", Double.NaN); 235 | float Y = warp.getFloat("yaw", Float.NaN); 236 | float P = warp.getFloat("pitch", Float.NaN); 237 | if (world == null || server == null || Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Float.isNaN(Y) || Float.isNaN(P)) { 238 | ShareData.getLogger().warning(String.format("[WARPS] 错误的warp数据: %s %s [%s, %s, %s] [%s,%s]", server, world, x, y, z, Y, P)); 239 | } else { 240 | WARPS.put(name, new ShareLocation(x, y, z, Y, P, world, server)); 241 | } 242 | } 243 | } 244 | 245 | @Override 246 | protected void save0() throws IOException { 247 | Configuration warps = new Configuration(); 248 | for (Map.Entry e : WARPS.entrySet()) { 249 | String name = e.getKey(); 250 | ShareLocation warp = e.getValue(); 251 | warps.set(name + ".world", warp.getWorld()); 252 | warps.set(name + ".server", warp.getServer()); 253 | warps.set(name + ".x", warp.getX()); 254 | warps.set(name + ".y", warp.getY()); 255 | warps.set(name + ".z", warp.getZ()); 256 | warps.set(name + ".yaw", warp.getYaw()); 257 | warps.set(name + ".pitch", warp.getPitch()); 258 | } 259 | YAML.save(warps, getFile()); 260 | } 261 | 262 | }; 263 | 264 | /** Yaml处理器 */ 265 | protected static final ConfigurationProvider YAML = ConfigurationProvider.getProvider(YamlConfiguration.class); 266 | /** 保存延时 */ 267 | private static final EnumMap SAVE_DELAY = new EnumMap<>(ConfFile.class); 268 | /** 文件名 */ 269 | protected final String fname; 270 | 271 | /** @return file */ 272 | public File getFile() { 273 | val folder = Main.getMain().getDataFolder(); 274 | return folder.resolve(fname).toFile(); 275 | } 276 | 277 | /** 加载 */ 278 | protected void load() { 279 | try { 280 | load0(); 281 | } catch (FileNotFoundException e) { 282 | // ignore 283 | } catch (IOException e) { 284 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 285 | e.printStackTrace(); 286 | } 287 | } 288 | 289 | /** 290 | * 实际加载 291 | * 292 | * @throws IOException IOE 293 | */ 294 | protected abstract void load0() throws IOException; 295 | 296 | /** 保存 */ 297 | protected void save() { 298 | try { 299 | save0(); 300 | } catch (IOException e) { 301 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 302 | e.printStackTrace(); 303 | } 304 | } 305 | 306 | /** 307 | * 实际保存 308 | * 309 | * @throws IOException IOE 310 | */ 311 | protected abstract void save0() throws IOException; 312 | } 313 | 314 | /** 315 | * 玩家配置文件
316 | * load: 被动式加载, 由LRU调用, 通过 {@link #load(Object, ThrowableFunction) 框架函数} 317 | * 调用实际加载函数, 加载数据
318 | * save: 被动式保存, 由LRU调用, 通过 {@link #save(UUID, ThrowableRunnable) 框架函数} 调用实际保存函数, 319 | * 保存数据
320 | * save: 主动式保存, 通过 {@link #save(UUID)} 触发, 由具体配置指定LRU, 调用其保存函数 321 | * 322 | * @author yuanlu 323 | */ 324 | @Getter 325 | @AllArgsConstructor 326 | public enum PlayerConfFile { 327 | /** 传送家 */ 328 | HOME("home.yml") { 329 | @Override 330 | protected void save(UUID u) { 331 | HashMap map = HOMES.check(u); 332 | if (map != null) HOMES.clearHandle(u, map); 333 | } 334 | }; 335 | 336 | /** Yaml处理器 */ 337 | protected static final ConfigurationProvider YAML = ConfigurationProvider.getProvider(YamlConfiguration.class); 338 | /** 文件名 */ 339 | protected final String fname; 340 | /** 需要保存 */ 341 | protected final HashSet needSave = new HashSet<>(); 342 | /** 保存延时 */ 343 | private final ConcurrentHashMap SAVE_DELAY = new ConcurrentHashMap<>(); 344 | 345 | /** 346 | * @param u UUID 347 | * @param mk 是否创建文件夹 348 | * 349 | * @return file 350 | */ 351 | public File getFile(UUID u, boolean mk) { 352 | val folder = Main.getMain().getDataFolder(); 353 | val uuid = u.toString(); 354 | folder.resolve(uuid.substring(0, 2)).resolve(uuid); 355 | if (mk) folder.toFile().mkdirs(); 356 | return folder.resolve(fname).toFile(); 357 | } 358 | 359 | /** 360 | * 加载 361 | * 362 | * @param T 363 | * @param R 364 | * @param t 输入数据 365 | * @param r 运行体 366 | * 367 | * @return result 368 | */ 369 | protected R load(T t, ThrowableFunction r) { 370 | try { 371 | return r.apply(t); 372 | } catch (FileNotFoundException e) { 373 | // ignore 374 | } catch (IOException e) { 375 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 376 | e.printStackTrace(); 377 | } 378 | return null; 379 | } 380 | 381 | /** 382 | * 强制保存
383 | * 由配置文件实现 384 | * 385 | * @param u UUID 386 | */ 387 | protected abstract void save(UUID u); 388 | 389 | /** 390 | * 保存 391 | * 392 | * @param u UUID 393 | * @param r 运行体 394 | */ 395 | protected void save(UUID u, ThrowableRunnable r) { 396 | if (!needSave.remove(u)) return; 397 | try { 398 | r.run(); 399 | } catch (FileNotFoundException e) { 400 | // ignore 401 | } catch (IOException e) { 402 | ShareData.getLogger().warning("[Conf] " + fname + ":"); 403 | e.printStackTrace(); 404 | } 405 | } 406 | } 407 | 408 | /** 409 | * Home 410 | * 411 | * @author yuanlu 412 | */ 413 | public static final class HomesLRU extends LRUCache> { 414 | 415 | /** 配置文件 */ 416 | private static final PlayerConfFile HOME = PlayerConfFile.HOME; 417 | 418 | /** @param size size */ 419 | private HomesLRU(int size) { 420 | super(size); 421 | } 422 | 423 | @Override 424 | protected void clearHandle(UUID k, HashMap v) { 425 | HOME.save(k, () -> save0(k, v)); 426 | } 427 | 428 | @Override 429 | protected HashMap create(UUID k) { 430 | HashMap data = HOME.load(k, this::load0); 431 | return data == null ? new HashMap<>() : data; 432 | } 433 | 434 | /** 435 | * load 436 | * 437 | * @param uid UUID 438 | * 439 | * @return data 440 | * 441 | * @throws IOException IOE 442 | */ 443 | private HashMap load0(@NonNull UUID uid) throws IOException { 444 | HashMap m = new HashMap<>(); 445 | val f = HOME.getFile(uid, false); 446 | val warps = PlayerConfFile.YAML.load(f); 447 | for (val name : warps.getKeys()) { 448 | val warp = warps.getSection(name); 449 | 450 | val world = warp.getString("world", null); 451 | val server = warp.getString("server", null); 452 | val x = warp.getDouble("x", Double.NaN); 453 | val y = warp.getDouble("y", Double.NaN); 454 | val z = warp.getDouble("z", Double.NaN); 455 | val Y = warp.getFloat("yaw", Float.NaN); 456 | val P = warp.getFloat("pitch", Float.NaN); 457 | if (world == null || server == null || Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z) || Float.isNaN(Y) || Float.isNaN(P)) { 458 | ShareData.getLogger() 459 | .warning(String.format("[HOMES] 错误的home数据: %s: %s %s [%s, %s, %s] [%s,%s]", f.getName(), server, world, x, y, z, Y, P)); 460 | } else { 461 | m.put(name, new ShareLocation(x, y, z, Y, P, world, server)); 462 | } 463 | } 464 | return m; 465 | } 466 | 467 | /** 468 | * save 469 | * 470 | * @param uid uuid 471 | * @param map data 472 | * 473 | * @throws IOException IOE 474 | */ 475 | private void save0(@NonNull UUID uid, HashMap map) throws IOException { 476 | val warps = new Configuration(); 477 | for (val e : map.entrySet()) { 478 | val name = e.getKey(); 479 | val warp = e.getValue(); 480 | warps.set(name + ".world", warp.getWorld()); 481 | warps.set(name + ".server", warp.getServer()); 482 | warps.set(name + ".x", warp.getX()); 483 | warps.set(name + ".y", warp.getY()); 484 | warps.set(name + ".z", warp.getZ()); 485 | warps.set(name + ".yaw", warp.getYaw()); 486 | warps.set(name + ".pitch", warp.getPitch()); 487 | } 488 | PlayerConfFile.YAML.save(warps, PlayerConfFile.HOME.getFile(uid, true)); 489 | } 490 | } 491 | } 492 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/main/java/yuan/plugins/serverDo/velocity/Core.java: -------------------------------------------------------------------------------- 1 | /** 2 | * yuanlu 3 | * date: 2021年8月11日 4 | * file: Core.java 5 | * gitu: yuanlu 6 | * gite: 2573580691@qq.com 7 | */ 8 | package yuan.plugins.serverDo.velocity; 9 | 10 | import com.velocitypowered.api.proxy.ConnectionRequestBuilder.Status; 11 | import com.velocitypowered.api.proxy.Player; 12 | import com.velocitypowered.api.proxy.ServerConnection; 13 | import com.velocitypowered.api.proxy.server.ServerInfo; 14 | import lombok.AccessLevel; 15 | import lombok.NoArgsConstructor; 16 | import lombok.NonNull; 17 | import lombok.val; 18 | import yuan.plugins.serverDo.Channel; 19 | import yuan.plugins.serverDo.ShareData; 20 | import yuan.plugins.serverDo.ShareLocation; 21 | import yuan.plugins.serverDo.Tool; 22 | import yuan.plugins.serverDo.velocity.ConfigManager.ConfFile; 23 | import yuan.plugins.serverDo.velocity.ConfigManager.PlayerConfFile; 24 | 25 | import java.util.HashMap; 26 | import java.util.HashSet; 27 | import java.util.UUID; 28 | import java.util.function.Function; 29 | 30 | /** 31 | * BC端核心 32 | * 33 | * @author yuanlu 34 | */ 35 | @NoArgsConstructor(access = AccessLevel.PACKAGE) 36 | public class Core { 37 | /** 版本不正确的服务器 */ 38 | static final HashSet BAD_SERVER = new HashSet<>(); 39 | /** 时间戳修正 */ 40 | static final HashMap TIME_AMEND = new HashMap<>(); 41 | 42 | /** 时间修正回调等待 */ 43 | static final HashMap TIME_AMEND_WAITER = new HashMap<>(); 44 | 45 | /** 当前隐身 */ 46 | static final HashSet nowVanish = new HashSet<>(); 47 | /** 自动隐身 */ 48 | static final HashSet alwaysVanish = new HashSet<>(); 49 | 50 | /** 51 | * 自动隐身处理 52 | * 53 | * @param player 玩家 54 | * @param server 玩家所在服务器 55 | */ 56 | public static void autoVanish(Player player, ServerConnection server) { 57 | val u = player.getUniqueId(); 58 | if (alwaysVanish.contains(u)) { 59 | nowVanish.add(u); 60 | Main.send(server, Channel.Vanish.sendC(true)); 61 | } 62 | } 63 | 64 | /** 65 | * 检查TP 66 | * 67 | * @param isSenior 是否是高级传送 68 | * @param myServer 本服务器 69 | * @param target 目标玩家 70 | * 71 | * @return 是否可以传送 72 | */ 73 | public static boolean canTp(boolean isSenior, String myServer, Player target) { 74 | val server = target.getCurrentServer().orElse(null); 75 | if (server == null) return false; 76 | val targetServer = server.getServerInfo().getName(); 77 | if (!ConfigManager.allowServer(targetServer)) return false; 78 | if (isSenior) return true; 79 | 80 | if (!ConfigManager.canTp(myServer, targetServer) || hasVanish(target)) return false; 81 | 82 | return true; 83 | } 84 | 85 | /** 86 | * 检查服务器间是否可以传送 87 | * 88 | * @param myServer 本服务器 89 | * @param targetServer 目标服务器 90 | * 91 | * @return 是否可以传送 92 | */ 93 | public static boolean canTp(String myServer, String targetServer) { 94 | if (!ConfigManager.allowServer(targetServer) || !ConfigManager.canTp(myServer, targetServer)) return false; 95 | return true; 96 | } 97 | 98 | /** 99 | * 获取地标 100 | * 101 | * @param player 玩家 102 | * @param name 名称 103 | * 104 | * @return 坐标 105 | */ 106 | public static ShareLocation getHome(@NonNull Player player, @NonNull String name) { 107 | return ConfigManager.HOMES.get(player.getUniqueId()).get(name); 108 | } 109 | 110 | /** 111 | * 获取家集合 112 | * 113 | * @param player 玩家 114 | * 115 | * @return homes 116 | */ 117 | public static HashMap getHomes(@NonNull Player player) { 118 | return ConfigManager.HOMES.get(player.getUniqueId()); 119 | } 120 | 121 | /** 122 | * 获取本服务器比子服务器快多少毫秒 123 | * 124 | * @param server 服务器 125 | * 126 | * @return 时间戳修正 127 | */ 128 | public static long getTimeAmend(ServerInfo server) { 129 | if (server == null) return 0; 130 | val amend = TIME_AMEND.get(server.getName()); 131 | return amend == null ? 0 : amend; 132 | } 133 | 134 | /** 135 | * 获取地标 136 | * 137 | * @param name 名称 138 | * 139 | * @return 坐标 140 | */ 141 | public static ShareLocation getWarp(@NonNull String name) { 142 | return ConfigManager.WARPS.get(name); 143 | } 144 | 145 | /** 146 | * 是否拥有隐身 147 | * 148 | * @param player 玩家 149 | * 150 | * @return 是否拥有隐身 151 | */ 152 | public static boolean hasVanish(Player player) { 153 | return nowVanish.add(player.getUniqueId()); 154 | } 155 | 156 | /** 157 | * 搜索家 158 | * 159 | * @param player 玩家 160 | * @param name 搜索名 161 | * 162 | * @return 匹配名 163 | */ 164 | public static String searchHome(@NonNull Player player, @NonNull String name) { 165 | return Tool.search(name, ConfigManager.HOMES.get(player.getUniqueId()).keySet().iterator()); 166 | } 167 | 168 | /** 169 | * 搜索地标 170 | * 171 | * @param name 搜索名 172 | * 173 | * @return 匹配名 174 | */ 175 | public static String searchWarp(@NonNull String name) { 176 | return Tool.search(name, ConfigManager.WARPS.keySet().iterator()); 177 | } 178 | 179 | /** 180 | * 设置/删除家 181 | * 182 | * @param player 玩家 183 | * @param name 家名称 184 | * @param loc 家坐标 185 | * @param amount 家最大数量 186 | * 187 | * @return true: 成功删除/成功设置
188 | * false:不存在/已满 189 | */ 190 | public static boolean setHome(@NonNull Player player, @NonNull String name, ShareLocation loc, int amount) { 191 | val home = ConfigManager.HOMES.get(player.getUniqueId()); 192 | boolean result; 193 | if (loc == null) result = home.remove(name) != null; 194 | else if ((loc = loc.clone()).getServer() == null) throw new IllegalArgumentException("[HOME] Null sever: " + name); 195 | else { 196 | result = (home.containsKey(name) || home.size() < amount); 197 | if (result) home.put(name, loc); 198 | } 199 | 200 | ConfigManager.saveConf(PlayerConfFile.HOME, player); 201 | return result; 202 | } 203 | 204 | /** 205 | * 设置家
206 | * 用于转换时上传 207 | * 208 | * @param player 玩家 209 | * @param name 家名称 210 | * @param loc 家坐标 211 | * 212 | * @see TransHandler#receiveHome(Player, 213 | * yuan.plugins.serverDo.Channel.TransHome) 214 | */ 215 | public static void setHome(@NonNull UUID player, @NonNull String name, @NonNull ShareLocation loc) { 216 | val home = ConfigManager.HOMES.get(player); 217 | if ((loc = loc.clone()).getServer() == null) throw new IllegalArgumentException("[HOME] Null sever: " + name); 218 | else home.put(name, loc); 219 | 220 | ConfigManager.saveConf(PlayerConfFile.HOME, player); 221 | } 222 | 223 | /** 224 | * 设置/删除地标 225 | * 226 | * @param name 地标名称 227 | * @param loc 地标坐标 228 | * 229 | * @return true: 成功删除/覆盖
230 | * false:不存在/新建 231 | */ 232 | public static boolean setWarp(@NonNull String name, ShareLocation loc) { 233 | boolean result; 234 | if (loc == null) result = ConfigManager.WARPS.remove(name) != null; 235 | else if ((loc = loc.clone()).getServer() == null) throw new IllegalArgumentException("[WARP] Null sever: " + name); 236 | else result = ConfigManager.WARPS.put(name, loc) != null; 237 | ConfigManager.saveConf(ConfFile.WARP); 238 | return result; 239 | } 240 | 241 | /** 242 | * 开始时间修正检测 243 | */ 244 | public static void startTimeAmend() { 245 | val pack = Channel.TimeAmend.sendC(); 246 | for (val server : Main.getMain().getProxy().getAllServers()) { 247 | if (server.getPlayersConnected().isEmpty()) continue; 248 | TIME_AMEND_WAITER.put(server.getServerInfo().getName(), System.currentTimeMillis()); 249 | Main.send(server, pack); 250 | } 251 | } 252 | 253 | /** 254 | * 切换隐身 255 | * 256 | * @param player 玩家 257 | * @param isAlways 是否自动隐身 258 | * 259 | * @return 切换后状态 260 | */ 261 | public static boolean switchVanish(Player player, boolean isAlways) { 262 | val set = isAlways ? alwaysVanish : nowVanish; 263 | val u = player.getUniqueId(); 264 | boolean r; 265 | if (!(r = set.add(u))) set.remove(u); 266 | ConfigManager.saveConf(ConfFile.ALWAYS_VANISH); 267 | return r; 268 | } 269 | 270 | /** 271 | * 时间修正回调 272 | * 273 | * @param info 子服务器 274 | * @param client 子服务器时间 275 | */ 276 | public static void timeAmendCallback(ServerInfo info, long client) { 277 | val start = TIME_AMEND_WAITER.remove(info.getName()); 278 | if (start == null) return; 279 | val end = System.currentTimeMillis(); 280 | final long time = end - start; 281 | if (time > 100) { 282 | ShareData.getLogger().warning("[时间修正] " + info.getName() + " 通信时间过长: " + time); 283 | return; 284 | } 285 | long shift = end - (client + time / 2); 286 | TIME_AMEND.put(info.getName(), shift); 287 | if (ShareData.isDEBUG()) ShareData.getLogger().info("[时间修正] " + info.getName() + " 偏移量:" + shift + ", 通信时长: " + time); 288 | } 289 | 290 | /** 291 | * 传送坐标 292 | * 293 | * @param player 玩家 294 | * @param loc 目标点 295 | * @param callback 传送回调数据生成 296 | */ 297 | public static void tpLocation(@NonNull Player player, ShareLocation loc, @NonNull Function callback) { 298 | val nowServer = player.getCurrentServer() 299 | .orElseThrow(() -> new IllegalStateException("Player \"" + player.getUsername() + "\" is not connected to any server.")); 300 | val targetServer = loc == null ? null : Main.getMain().getProxy().getServer(loc.getServer()).orElse(null); 301 | if (targetServer == null) { 302 | Main.send(nowServer, callback.apply(false)); 303 | return; 304 | } 305 | Main.sendQueue(targetServer, Channel.TpLoc.s1S_tpLoc(loc, player.getUsername())); 306 | if (nowServer.getServerInfo().getName().equals(targetServer.getServerInfo().getName())) { 307 | Main.send(nowServer, callback.apply(true)); 308 | } else player.createConnectionRequest(targetServer).connect().thenAcceptAsync(r -> { 309 | val success = r.getStatus() == Status.SUCCESS || r.getStatus() == Status.ALREADY_CONNECTED; 310 | Main.send(nowServer, callback.apply(success)); 311 | }); 312 | } 313 | 314 | } 315 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/main/java/yuan/plugins/serverDo/velocity/TabHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.velocity; 5 | 6 | import com.velocitypowered.api.event.player.TabCompleteEvent; 7 | import com.velocitypowered.api.proxy.Player; 8 | import lombok.AccessLevel; 9 | import lombok.NoArgsConstructor; 10 | import lombok.NonNull; 11 | import lombok.val; 12 | import yuan.plugins.serverDo.ShareData; 13 | import yuan.plugins.serverDo.ShareData.TabType; 14 | 15 | import java.util.List; 16 | import java.util.stream.Stream; 17 | 18 | /** 19 | * tab处理器 20 | * 21 | * @author yuanlu 22 | */ 23 | @SuppressWarnings("javadoc") 24 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 25 | public final class TabHandler { 26 | private static void onAt(Player player, String request, List list) { 27 | if (!ConfigManager.isUseAt()) { 28 | list.add("@" + request); 29 | return; 30 | } 31 | 32 | Stream stream = Main.getMain().getProxy().getAllPlayers()// 33 | .stream()// 34 | .map(Player::getUsername)// 35 | .map(String::toLowerCase); 36 | if (!request.isEmpty()) stream = stream.filter(request::startsWith); 37 | 38 | stream.map("@"::concat).forEach(list::add); 39 | } 40 | 41 | private static void onHome(Player player, String request, List list) { 42 | val server = player.getCurrentServer().orElse(null); 43 | if (server == null) return; 44 | val serverName = server.getServerInfo().getName(); 45 | Core.getHomes(player).forEach((name, loc) -> { 46 | if (name.toLowerCase().startsWith(request) && Core.canTp(serverName, loc.getServer()))// 47 | list.add(name); 48 | }); 49 | } 50 | 51 | private static void onTp(Player player, String request, List list, boolean isAll) { 52 | val server = player.getCurrentServer().orElse(null); 53 | if (server == null) return; 54 | val serverName = server.getServerInfo().getName(); 55 | if (ConfigManager.allowServer(serverName)) for (val p : Main.getMain().getProxy().getAllPlayers()) { 56 | val name = p.getUsername(); 57 | if (name.toLowerCase().startsWith(request) && Core.canTp(isAll, serverName, p)) // 58 | list.add(name); 59 | } 60 | } 61 | 62 | private static void onWarp(Player player, String request, List list) { 63 | val server = player.getCurrentServer().orElse(null); 64 | if (server == null) return; 65 | val serverName = server.getServerInfo().getName(); 66 | ConfigManager.WARPS.forEach((name, loc) -> { 67 | if (name.toLowerCase().startsWith(request) && Core.canTp(serverName, loc.getServer())) list.add(name); 68 | }); 69 | } 70 | 71 | /** 72 | * EVENT 73 | * 74 | * @param list 补全响应 75 | */ 76 | public static void TabComplete(Player player, @NonNull List list) { 77 | if (list.size() != 1 || player == null) return; 78 | val str = list.get(0); 79 | if (str != null) { 80 | val type = TabType.getType(ConfigManager.getTabReplace(), str); 81 | val request = TabType.getValue(ConfigManager.getTabReplace(), str); 82 | if (type == null || request == null) return; 83 | list.clear(); 84 | switch (type) { 85 | case TP_ALL: 86 | onTp(player, request, list, true); 87 | break; 88 | case TP_NORMAL: 89 | onTp(player, request, list, false); 90 | break; 91 | case WARP: 92 | onWarp(player, request, list); 93 | break; 94 | case HOME: 95 | onHome(player, request, list); 96 | break; 97 | case AT: 98 | onAt(player, request, list); 99 | break; 100 | 101 | } 102 | if (ShareData.isDEBUG()) ShareData.getLogger().info(String.format("Tab: type=%s, req=%s, list=%s", type, request, list)); 103 | if (list.isEmpty()) list.add(request); 104 | } 105 | } 106 | 107 | /** 108 | * EVENT 109 | * 110 | * @param e 补全响应 111 | */ 112 | public static void TabComplete(TabCompleteEvent e) { 113 | val list = e.getSuggestions(); 114 | TabComplete(e.getPlayer(), list); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/main/java/yuan/plugins/serverDo/velocity/TransHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package yuan.plugins.serverDo.velocity; 5 | 6 | import com.velocitypowered.api.proxy.Player; 7 | import lombok.AccessLevel; 8 | import lombok.NoArgsConstructor; 9 | import yuan.plugins.serverDo.Channel; 10 | import yuan.plugins.serverDo.Channel.TransHome; 11 | import yuan.plugins.serverDo.Channel.TransWarp; 12 | import yuan.plugins.serverDo.ShareLocation; 13 | 14 | import java.util.HashMap; 15 | import java.util.LinkedHashMap; 16 | import java.util.UUID; 17 | import java.util.function.Function; 18 | 19 | /** 20 | * 转换处理器 21 | * 22 | * @author yuanlu 23 | */ 24 | @NoArgsConstructor(access = AccessLevel.PRIVATE) 25 | public final class TransHandler { 26 | /** 所有接收到的家 */ 27 | private static final HashMap> HOMES = new HashMap<>(); 28 | /** 所有接收到的家 */ 29 | private static final Function> HOMES_MF = x -> new LinkedHashMap<>(); 30 | /** 所有接收到的地标 */ 31 | private static final LinkedHashMap WARPS = new LinkedHashMap<>(); 32 | /** 所有接收到的家计数器 */ 33 | private static int HOMES_COUNTER = 0; 34 | /** 所有接收到的地标计数器 */ 35 | private static int WARPS_COUNTER = 0; 36 | 37 | /** 38 | * 接收家数据 39 | * 40 | * @param player 通道玩家 41 | * @param home 家信息 42 | */ 43 | public static synchronized void receiveHome(Player player, TransHome home) { 44 | if (home == null) { 45 | Main.send(player, Channel.TransHome.sendC(HOMES_COUNTER)); 46 | HOMES_COUNTER = 0; 47 | 48 | HOMES.forEach((uuid, homes) -> // 49 | homes.forEach((name, loc) -> // 50 | Core.setHome(uuid, name, loc))); 51 | HOMES.clear(); 52 | } else { 53 | HOMES_COUNTER++; 54 | HOMES.computeIfAbsent(home.getPlayer(), HOMES_MF).put(home.getName(), setServer(player, home.getLoc())); 55 | } 56 | } 57 | 58 | /** 59 | * 接收地标数据 60 | * 61 | * @param player 通道玩家 62 | * @param warp 地标信息 63 | */ 64 | public static synchronized void receiveWarp(Player player, TransWarp warp) { 65 | if (warp == null) { 66 | Main.send(player, Channel.TransWarp.sendC(WARPS_COUNTER)); 67 | WARPS_COUNTER = 0; 68 | 69 | WARPS.forEach(Core::setWarp); 70 | WARPS.clear(); 71 | } else { 72 | WARPS_COUNTER++; 73 | WARPS.put(warp.getName(), setServer(player, warp.getLoc())); 74 | } 75 | } 76 | 77 | /** 78 | * 设置坐标所在的服务器 79 | * 80 | * @param player 玩家 81 | * @param loc 坐标 82 | * 83 | * @return 传入的坐标 84 | */ 85 | private static ShareLocation setServer(Player player, ShareLocation loc) { 86 | loc.setServer(player// 87 | .getCurrentServer()// 88 | .orElseThrow(() -> new IllegalStateException("Player \"" + player.getUsername() + "\" is not connected to any server."))// 89 | .getServerInfo()// 90 | .getName()); 91 | return loc; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/main/resources/proxy-config.yml: -------------------------------------------------------------------------------- 1 | #跨服传送组 2 | server-group: 3 | #同一组内可以使用/tpa, /tpahere等命令 4 | group1: 5 | - server1 6 | - server2 7 | group2: 8 | - server3 9 | - server4 10 | #禁用的服务器, 例如登录服务器 11 | server-ban: 12 | - 'auth' 13 | - 'lobby' 14 | 15 | #名称获取设置 16 | setting: 17 | #如果设置为true,在允许的情况下,从子服获取玩家名称,否则全由bungeecord获取玩家名称 18 | sub-server-display-name: false 19 | 20 | #玩家tab替换内容,可任意修改 21 | player-tab-replace: "%%%yuanlu-tab-player-list" 22 | 23 | #时间校准循环间隔(ms) 24 | timeAmend: 300000 25 | 26 | #自动保存延时(ms) 27 | save-delay: 60000 28 | 29 | #是否启用at 30 | use-at: true 31 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/main/resources/velocity-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "yuanlu-server-do", 3 | "name": "yuanluServerDo", 4 | "version": "${parent.version}", 5 | "url": "${parent.url}", 6 | "authors": [ 7 | "yuanlu" 8 | ], 9 | "main": "yuan.plugins.serverDo.velocity.Main", 10 | "description": "${parent.description}" 11 | } 12 | -------------------------------------------------------------------------------- /yuanluServerDo-velocity/src/test/resources/velocity-plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "yuanlu-server-do", 3 | "name": "yuanluServerDo", 4 | "version": "${project.version}", 5 | "url": "https://github.com/MineYuanlu/yuanluServerDo", 6 | "authors": [ 7 | "yuanlu" 8 | ], 9 | "main": "yuan.plugins.serverDo.velocity.Main", 10 | "description": "${project.description}" 11 | } 12 | -------------------------------------------------------------------------------- /yuanluServerDo.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | --------------------------------------------------------------------------------