├── TextComponentPlugin ├── gradle.properties ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── textcomponentplugin │ │ ├── executor │ │ ├── PrintTitle.java │ │ ├── PrintKeyBind.java │ │ ├── PrintComponentBuilder.java │ │ ├── PrintBossBar.java │ │ └── PrintMessage.java │ │ └── TextComponentPlugin.java ├── build.gradle ├── .gitignore └── gradlew.bat ├── featureTest ├── src │ └── main │ │ ├── resources │ │ ├── dir │ │ │ └── test.yml │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── featuretest │ │ ├── event │ │ ├── CustomBowShootEvent.java │ │ └── RecipeCraftEvent.java │ │ ├── listener │ │ └── OnEatEventListener.java │ │ └── FeatureTest.java ├── featureTest.iml └── pom.xml ├── HelloWorld ├── src │ └── main │ │ ├── resources │ │ ├── config.yml │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── helloworld │ │ └── HelloWorld.java ├── README.md ├── HelloWorld.iml └── pom.xml ├── README.md ├── EventListener ├── README.md ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── eventlistener │ │ └── EventListener.java ├── .gitignore └── pom.xml ├── PluginWithDatabase ├── src │ └── main │ │ ├── resources │ │ ├── config.yml │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── pluginwithdatabase │ │ ├── mapper │ │ ├── PlayerMapper.java │ │ └── PlayerMapper.xml │ │ └── entity │ │ └── PlayerPO.java └── test.sql ├── BlockPlugin ├── README.md ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── blockplugin │ │ └── examples │ │ ├── blockdata │ │ ├── ApplyPhysicsExample.java │ │ └── BlockDataExample.java │ │ ├── block │ │ └── BlockExample.java │ │ └── blockstate │ │ └── BlockStateExample.java └── pom.xml ├── SimpleCustomEvent ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── simplecustomevent │ │ ├── SimpleCustomEventPlugin.java │ │ └── event │ │ ├── listener │ │ ├── OnPlayerSneakingHitSheepEventListener.java │ │ ├── TestEventListener.java │ │ └── caller │ │ │ └── PlayerSneakingHitSheepEventCaller.java │ │ └── PlayerSneakingHitSheepEvent.java └── pom.xml ├── CustomEventPlugin ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── senko │ │ │ └── customeventplugin │ │ │ ├── test │ │ │ └── Test.java │ │ │ ├── enums │ │ │ └── EnumBowMode.java │ │ │ ├── CustomEventPlugin.java │ │ │ ├── holder │ │ │ └── GodBowHolder.java │ │ │ ├── utils │ │ │ └── InventoryUtils.java │ │ │ ├── executor │ │ │ └── GodBowCommandExecutor.java │ │ │ └── event │ │ │ └── GodBowEvent.java │ │ └── resources │ │ └── plugin.yml ├── CustomEventPlugin.iml └── pom.xml ├── MapTutorialPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── maptutorialplugin │ │ ├── render │ │ └── FirstRender.java │ │ └── MapTutorialPlugin.java └── pom.xml ├── Configuration ├── src │ └── main │ │ ├── resources │ │ ├── plugin.yml │ │ └── config.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── configuration │ │ ├── listener │ │ └── LoginEventListener.java │ │ └── executor │ │ └── ChangeLocationExecutor.java ├── Configuration.iml └── pom.xml ├── ThreadPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── threadplugin │ │ ├── task │ │ ├── Task3.java │ │ ├── Task4.java │ │ └── Task2.java │ │ └── ThreadPlugin.java ├── .gitignore └── pom.xml ├── AttributePlugin ├── README.md ├── src │ └── main │ │ └── resources │ │ └── plugin.yml └── pom.xml ├── CustomRecipe ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── customrecipe │ │ ├── CustomRecipe.java │ │ └── executor │ │ └── VillagerCommandExecutor.java ├── CustomRecipe.iml └── pom.xml ├── InventoryPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── inventoryplugin │ │ ├── InventoryPlugin.java │ │ ├── holder │ │ └── GlobalInventory.java │ │ ├── listener │ │ └── CantGetFromChestListener.java │ │ └── executor │ │ └── RemoteInventoryCommandExecutor.java ├── InventoryPlugin.iml └── pom.xml ├── PotionPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── potion │ │ ├── PotionPlugin.java │ │ └── executor │ │ ├── GetPotion.java │ │ └── ApplyEffect.java └── pom.xml ├── ConversationPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── conversationplugin │ │ ├── ConversationPlugin.java │ │ └── conversation │ │ ├── prefix │ │ └── MyPromptPrefix.java │ │ ├── promts │ │ ├── FinalFriendMessagePrompt.java │ │ ├── SecondPrompt.java │ │ ├── FirstPrompt.java │ │ └── ThirdPrompt.java │ │ └── MyDefaultConversationFactory.java └── pom.xml ├── TabExecutorPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── tabexecutorplugin │ │ ├── TabExecutorPlugin.java │ │ └── commands │ │ ├── impl │ │ ├── GodModeCmd.java │ │ └── HelpCmd.java │ │ └── ICommand.java └── pom.xml ├── ParticleShootPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── github │ │ └── shinyoki │ │ └── particleshootplugin │ │ ├── factory │ │ └── BulletFactory.java │ │ ├── ParticleShootPlugin.java │ │ └── listener │ │ ├── cmd │ │ └── GetBulletCmd.java │ │ └── event │ │ └── BulletShootEventListener.java └── pom.xml ├── Command ├── Command.iml ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── senko │ │ │ └── command │ │ │ ├── commands │ │ │ ├── subCmdImpl │ │ │ │ ├── GodMode.java │ │ │ │ └── Help.java │ │ │ ├── executors │ │ │ │ ├── GodCommandExecutor.java │ │ │ │ └── MyCommand.java │ │ │ └── ICommand.java │ │ │ └── Command.java │ │ └── resources │ │ └── plugin.yml ├── README.md └── pom.xml ├── EasySqlTutorialPlugin ├── src │ └── main │ │ └── resources │ │ ├── config.yml │ │ └── plugin.yml ├── test.sql └── pom.xml ├── ScoreBoardPlugin ├── scoreBoardPlugin.iml ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── senko │ │ │ └── scoreboardplugin │ │ │ ├── strategy │ │ │ └── AbstractCreationStrategy.java │ │ │ ├── listener │ │ │ └── OnPlayerQuitHandler.java │ │ │ └── ScoreBoardPlugin.java │ │ └── resources │ │ └── plugin.yml └── pom.xml ├── PersistentPlugin ├── src │ └── main │ │ ├── resources │ │ └── plugin.yml │ │ └── java │ │ └── com │ │ └── senko │ │ └── persistentplugin │ │ └── data │ │ └── LocalDateTimeDataTypeImpl.java └── pom.xml ├── InvokeThirdPluginAPI └── src │ └── main │ └── resources │ └── plugin.yml └── pom.xml /TextComponentPlugin/gradle.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /featureTest/src/main/resources/dir/test.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /HelloWorld/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | senko: 2 | age: 13 -------------------------------------------------------------------------------- /HelloWorld/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | This is a quick start of Spigot plugin. -------------------------------------------------------------------------------- /TextComponentPlugin/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'TextComponentPlugin' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 你好~ 2 | 欢迎观光我的仓库,这是我的第一个正式仓库,专门用于记录开发Spigot Plugin的过程,代码写的很差,还请见谅 :2 3 | -------------------------------------------------------------------------------- /EventListener/README.md: -------------------------------------------------------------------------------- 1 | # 事件监听器 2 | 本项目的代码主要涉及到事件监听器的实现、注册和失效,涉及的的事件有两种,分别是玩家加入时的全体玩家提示和登陆玩家获得的音效,以及伤害羊时会改变羊毛的颜色。 -------------------------------------------------------------------------------- /TextComponentPlugin/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shinyoki/Minecraft-Spigot-learn/HEAD/TextComponentPlugin/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /PluginWithDatabase/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | senko: 2 | #数据源 3 | datasource: 4 | host: localhost 5 | port: 3306 6 | username: root 7 | password: root 8 | database: test -------------------------------------------------------------------------------- /BlockPlugin/README.md: -------------------------------------------------------------------------------- 1 | # 方块 2 | - [视频教程地址] (还没做) 3 | - [API文档地址](https://bukkit.windit.net/javadoc/org/bukkit/block/package-summary.html) 4 | > 主要涉及到的类:[Block, BlockState, BlockData, BlockFace, BlockData] -------------------------------------------------------------------------------- /BlockPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: BlockPlugin 2 | version: '${project.version}' 3 | main: com.senko.blockplugin.BlockPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 了解方块在插件中的体现 -------------------------------------------------------------------------------- /EventListener/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: EventListener 2 | version: '${project.version}' 3 | main: com.senko.eventlistener.EventListener 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 事件监听器 7 | -------------------------------------------------------------------------------- /HelloWorld/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: HelloWorld 2 | version: '${project.version}' 3 | main: com.senko.helloworld.HelloWorld 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 这是我的第一个Minecraft Spigot Plugin 7 | -------------------------------------------------------------------------------- /SimpleCustomEvent/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: SimpleCustomEventPlugin 2 | version: '${project.version}' 3 | main: com.senko.simplecustomevent.SimpleCustomEventPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 自定义事件 -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/java/com/senko/customeventplugin/test/Test.java: -------------------------------------------------------------------------------- 1 | package com.senko.customeventplugin.test; 2 | 3 | public class Test { 4 | public static void main(String[] args) { 5 | System.out.println(4 % 10); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /MapTutorialPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: MapTutorialPlugin 2 | version: '${project.version}' 3 | main: com.senko.maptutorialplugin.MapTutorialPlugin 4 | api-version: 1.18 5 | description: 快速上手了解Map API 6 | 7 | commands: 8 | get: 9 | description: 获取一个地图 -------------------------------------------------------------------------------- /Configuration/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Configuration 2 | version: '${project.version}' 3 | main: com.senko.configuration.Configuration 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 配置读取、设置 7 | commands: 8 | change: 9 | description: 改变location -------------------------------------------------------------------------------- /TextComponentPlugin/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /ThreadPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ThreadPlugin 2 | version: '${project.version}' 3 | main: com.senko.threadplugin.ThreadPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 测试Spigot插件的线程调用 7 | commands: 8 | stuck: 9 | description: 执行同步或异步的线程任务 -------------------------------------------------------------------------------- /AttributePlugin/README.md: -------------------------------------------------------------------------------- 1 | # 物品属性 Item Attribute 2 | - [视频教程地址](https://www.bilibili.com/video/BV1oZ4y1e7nJ)
3 | - [API文档地址](https://bukkit.windit.net/javadoc/org/bukkit/attribute/package-summary.html)
4 | > 主要涉及到的类:[Attribute, AttributeModifier, Operation, EquipmentSlot, ItemFlag]
5 | 6 | -------------------------------------------------------------------------------- /Configuration/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | senko: 2 | list: #list 3 | - 1 4 | - 2 5 | - abc 6 | map: #MapList 7 | - {name: senko, count: 1, speed: 3.3f} 8 | - {name: senko2, count: 2, speed: 2.2f} 9 | age: 11 #11 10 | disabled: false #false -------------------------------------------------------------------------------- /CustomRecipe/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: CustomRecipe 2 | version: '${project.version}' 3 | main: com.senko.customrecipe.CustomRecipe 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 自定义物品合成 7 | depend: [CustomEventPlugin] 8 | commands: 9 | villager: 10 | description: 生成一只特殊的村民 -------------------------------------------------------------------------------- /InventoryPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: InventoryPlugin 2 | version: '${project.version}' 3 | main: com.senko.inventoryplugin.InventoryPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 测试插件对物品栏的操作 7 | commands: 8 | open: 9 | description: 打开一个箱子 10 | usage: / -------------------------------------------------------------------------------- /PotionPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: PotionPlugin 2 | version: '${project.version}' 3 | main: com.senko.potion.PotionPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 了解自定义药水效果 7 | 8 | commands: 9 | potion: 10 | description: "附加药水效果" 11 | get-potion: 12 | description: "获取药水" -------------------------------------------------------------------------------- /featureTest/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: FeatureTest 2 | version: '${project.version}' 3 | main: com.senko.featuretest.FeatureTest 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 测试一些特性或理论问题 7 | commands: 8 | testL: 9 | description: 测试下Location对象的克隆问题 10 | usage: / [copy | clone] -------------------------------------------------------------------------------- /ConversationPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ConversationPlugin 2 | version: '${project.version}' 3 | main: com.senko.conversationplugin.ConversationPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 上手了解ConversationAPI 7 | commands: 8 | start: 9 | usage: / 10 | description: 开始一次问答会话 -------------------------------------------------------------------------------- /TabExecutorPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: TabExecutorPlugin 2 | version: '${project.version}' 3 | main: com.senko.tabexecutorplugin.TabExecutorPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 指令填充提示案例插件 7 | commands: 8 | info: 9 | description: 指令填充提示案例插件 10 | sub: 11 | description: 子指令执行器 -------------------------------------------------------------------------------- /ParticleShootPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ParticleShootPlugin 2 | version: '${project.version}' 3 | main: github.shinyoki.particleshootplugin.ParticleShootPlugin 4 | api-version: '1.20' 5 | commands: 6 | getbullet: 7 | description: Get bullet 8 | usage: / 9 | permission: particleshootplugin.getbullet -------------------------------------------------------------------------------- /AttributePlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: AttributePlugin 2 | version: '${project.version}' 3 | main: com.senko.attributeplugin.AttributePlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 为物品添加属性加成 7 | commands: 8 | boom: 9 | usage: "============AttributePlugin============\n 10 | /boom add \n 11 | /boom hide \n" -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: CustomEventPlugin 2 | version: '${project.version}' 3 | main: com.senko.customeventplugin.CustomEventPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 自定义事件 7 | commands: 8 | godbow: 9 | usage: /godbow 10 | description: 给自己发一把类似于Hypixel的Modular Bow 11 | permission: senko.godbow -------------------------------------------------------------------------------- /Command/Command.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /HelloWorld/HelloWorld.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Configuration/Configuration.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CustomRecipe/CustomRecipe.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /EasySqlTutorialPlugin/src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | senko: 2 | #数据源 3 | datasource: 4 | #驱动:默认为mysql8 5 | driver: com.mysql.cj.jdbc.Driver 6 | #连接 7 | url: jdbc:mysql://localhost:3306/easy?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true 8 | #数据库用户名 9 | username: root 10 | #数据库密码 11 | password: root 12 | -------------------------------------------------------------------------------- /InventoryPlugin/InventoryPlugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CustomEventPlugin/CustomEventPlugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ScoreBoardPlugin/scoreBoardPlugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /PersistentPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: PersistentPlugin 2 | version: '${project.version}' 3 | main: com.senko.persistentplugin.PersistentPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 了解数据持久化的另一种方式 7 | commands: 8 | meta: 9 | usage: "=============PersistentPlugin=============\n 10 | /meta add 向手中物品添加数据\n 11 | /meta get 获取手中物品的数据\n 12 | /meta mark 向目标中的方块添加自定义数据\n 13 | /meta query 获取目标方块中的自定义数据\n" -------------------------------------------------------------------------------- /ScoreBoardPlugin/src/main/java/com/senko/scoreboardplugin/strategy/AbstractCreationStrategy.java: -------------------------------------------------------------------------------- 1 | package com.senko.scoreboardplugin.strategy; 2 | 3 | import org.bukkit.entity.Player; 4 | 5 | /** 6 | * 计分板生成策略 7 | * @author senko 8 | * @date 2022/6/15 13:10 9 | */ 10 | public interface AbstractCreationStrategy { 11 | 12 | /** 13 | * 设置玩家的计分板 14 | * @param player 玩家 15 | */ 16 | void setScoreboard(Player player); 17 | } 18 | -------------------------------------------------------------------------------- /featureTest/featureTest.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /PluginWithDatabase/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: PluginWithDatabase 2 | version: '${project.version}' 3 | main: com.senko.pluginwithdatabase.PluginWithDatabase 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 了解在插件开发中调用数据库 7 | commands: 8 | db-mybatis: 9 | description: 数据库操作 10 | usage: "==========§6Db-Mybatis§f==========\n 11 | /db-mybatis query §6§f\n 12 | /db-mybatis insert §6§f\n 13 | /db-mybatis update §6 §f\n 14 | /db-mybatis delete §6§f\n" -------------------------------------------------------------------------------- /ThreadPlugin/src/main/java/com/senko/threadplugin/task/Task3.java: -------------------------------------------------------------------------------- 1 | package com.senko.threadplugin.task; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.bukkit.scheduler.BukkitRunnable; 5 | 6 | public class Task3 extends BukkitRunnable { 7 | private CommandSender sender; 8 | 9 | public Task3(CommandSender sender) { 10 | this.sender = sender; 11 | } 12 | 13 | @Override 14 | public void run() { 15 | sender.sendMessage("这是3s后的消息,当前时间戳:" + System.currentTimeMillis()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/java/com/senko/customeventplugin/enums/EnumBowMode.java: -------------------------------------------------------------------------------- 1 | package com.senko.customeventplugin.enums; 2 | 3 | public enum EnumBowMode { 4 | PUNCH(0, "击退"), 5 | LIGHTING(1, "雷击"); 6 | Integer mode; 7 | String modeName; 8 | 9 | EnumBowMode(Integer mode,String modeName) { 10 | this.mode = mode; 11 | this.modeName = modeName; 12 | } 13 | 14 | public Integer getMode() { 15 | return mode; 16 | } 17 | 18 | public String getModeName() { 19 | return modeName; 20 | } 21 | } -------------------------------------------------------------------------------- /EasySqlTutorialPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: EasySqlTutorial 2 | version: '${project.version}' 3 | main: com.senko.easysqltutorial.EasySqlTutorial 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 上手了解EasySql工具的简单应用 7 | commands: 8 | db-easy-sql: 9 | description: 了解EasySql工具的简单应用 10 | usage: "==========§6Db-Easy-Sql§f==========\n 11 | /db-easy-sql query §6§f\n 12 | /db-easy-sql insert §6§f\n 13 | /db-easy-sql update §6 §f\n 14 | /db-easy-sql delete §6§f\n" -------------------------------------------------------------------------------- /TabExecutorPlugin/src/main/java/com/senko/tabexecutorplugin/TabExecutorPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.tabexecutorplugin; 2 | 3 | import com.senko.tabexecutorplugin.executor.CommandHandler; 4 | import com.senko.tabexecutorplugin.executor.CommandTipExecutor; 5 | import org.bukkit.plugin.java.JavaPlugin; 6 | 7 | public final class TabExecutorPlugin extends JavaPlugin { 8 | 9 | @Override 10 | public void onEnable() { 11 | // 视频教学案例 12 | getCommand("info").setExecutor(new CommandTipExecutor()); 13 | 14 | //子指令执行器 15 | getCommand("sub").setExecutor(new CommandHandler()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /InvokeThirdPluginAPI/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: InvokeThirdPluginAPI 2 | version: '${project.version}' 3 | main: com.senko.invokethirdpluginapi.InvokeThirdPluginAPI 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 了解如何调用其他插件提供的API,在本期视频里,我用到的是VaultAPI,经济插件为EssentialX 7 | commands: 8 | senko: 9 | usage: "===========myDynamic===========\n 10 | /senko add 添加金钱\n 11 | /senko remove 减少金钱\n 12 | /senko get 查询金钱数量\n" 13 | # 声明必要的前置插件(服务端会检查有没有该插件正在运行,如果没有则不会启用当前插件) 14 | depend: 15 | - Vault 16 | # 可忽略的前置依赖 (这里即使我随便地写,当前插件都能被服务端正常启用) 17 | softdepend: 18 | - PlaceholderAPI -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/java/com/senko/customeventplugin/CustomEventPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.customeventplugin; 2 | 3 | import com.senko.customeventplugin.executor.GodBowCommandExecutor; 4 | import com.senko.customeventplugin.listener.GodBowEventListener; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | public final class CustomEventPlugin extends JavaPlugin { 9 | @Override 10 | public void onEnable() { 11 | getCommand("godbow").setExecutor(new GodBowCommandExecutor()); 12 | Bukkit.getPluginManager().registerEvents(new GodBowEventListener(),this); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /InventoryPlugin/src/main/java/com/senko/inventoryplugin/InventoryPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.inventoryplugin; 2 | 3 | import com.senko.inventoryplugin.executor.RemoteInventoryCommandExecutor; 4 | import com.senko.inventoryplugin.listener.CantGetFromChestListener; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | public final class InventoryPlugin extends JavaPlugin { 9 | 10 | @Override 11 | public void onEnable() { 12 | getCommand("open").setExecutor(new RemoteInventoryCommandExecutor()); 13 | Bukkit.getServer().getPluginManager().registerEvents(new CantGetFromChestListener(),this); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /HelloWorld/src/main/java/com/senko/helloworld/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package com.senko.helloworld; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.configuration.file.FileConfiguration; 5 | import org.bukkit.plugin.java.JavaPlugin; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.logging.Logger; 10 | 11 | public final class HelloWorld extends JavaPlugin { 12 | 13 | @Override 14 | public void onEnable() { 15 | System.out.println(ChatColor.GREEN + "==========HelloWorld2============"); 16 | System.out.println(ChatColor.GREEN + "HelloWorld2"); 17 | getLogger().info(ChatColor.GREEN + "HelloWorld2"); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /EventListener/src/main/java/com/senko/eventlistener/EventListener.java: -------------------------------------------------------------------------------- 1 | package com.senko.eventlistener; 2 | 3 | import com.senko.eventlistener.listener.MyEventHandler; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.plugin.java.JavaPlugin; 6 | 7 | public final class EventListener extends JavaPlugin { 8 | 9 | @Override 10 | public void onEnable() { 11 | // 需要在Server的插件管理器中注册事件监听器。 12 | this.getServer().getPluginManager().registerEvents(new MyEventHandler(),this); 13 | 14 | //Bukkit.getPluginManager().registerEvents(new MyEventHandler(),this); 15 | } 16 | 17 | @Override 18 | public void onDisable() { 19 | // Plugin shutdown logic 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ThreadPlugin/src/main/java/com/senko/threadplugin/task/Task4.java: -------------------------------------------------------------------------------- 1 | package com.senko.threadplugin.task; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.bukkit.scheduler.BukkitRunnable; 5 | 6 | import java.util.concurrent.atomic.AtomicInteger; 7 | 8 | public class Task4 extends BukkitRunnable { 9 | private AtomicInteger integer = new AtomicInteger(0); 10 | private CommandSender sender; 11 | 12 | public Task4(CommandSender sender) { 13 | this.sender = sender; 14 | } 15 | 16 | @Override 17 | public void run() { 18 | sender.sendMessage("这是第" + integer.getAndIncrement() + "次执行,当前时间戳:" + System.currentTimeMillis() + "是不是与上次发送间隔20ticks?(20ticks == 1s == 1000 Timestamp)"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ThreadPlugin/src/main/java/com/senko/threadplugin/ThreadPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.threadplugin; 2 | 3 | 4 | import com.senko.threadplugin.executor.ThreadCommandExecutor; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | import org.bukkit.scheduler.BukkitRunnable; 8 | import org.bukkit.scheduler.BukkitScheduler; 9 | 10 | 11 | public final class ThreadPlugin extends JavaPlugin { 12 | private static ThreadPlugin instance; 13 | 14 | public static ThreadPlugin getInstance() { 15 | return instance; 16 | } 17 | @Override 18 | public void onEnable() { 19 | instance = this; 20 | 21 | getCommand("stuck").setExecutor(new ThreadCommandExecutor()); 22 | 23 | 24 | } 25 | 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /ThreadPlugin/src/main/java/com/senko/threadplugin/task/Task2.java: -------------------------------------------------------------------------------- 1 | package com.senko.threadplugin.task; 2 | 3 | import org.bukkit.command.CommandSender; 4 | import org.bukkit.plugin.Plugin; 5 | import org.bukkit.scheduler.BukkitRunnable; 6 | 7 | /** 8 | * 请使用异步函数执行 9 | * 10 | * sleep 3s 11 | */ 12 | public class Task2 extends BukkitRunnable { 13 | private CommandSender sender; 14 | 15 | public Task2(CommandSender sender) { 16 | this.sender = sender; 17 | } 18 | 19 | @Override 20 | public void run() { 21 | sender.sendMessage("虽然这里Thread.sleep了3s,但是该任务被异步执行了,并不会影响主线程,此时你仍然能收到服务器的响应。"); 22 | try { 23 | Thread.sleep(3000); 24 | } catch (InterruptedException e) { 25 | e.printStackTrace(); 26 | } finally { 27 | sender.sendMessage("3s已到!"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/java/com/senko/customeventplugin/holder/GodBowHolder.java: -------------------------------------------------------------------------------- 1 | package com.senko.customeventplugin.holder; 2 | 3 | import com.senko.customeventplugin.pojo.GodBow; 4 | import org.bukkit.entity.Entity; 5 | import org.bukkit.inventory.ItemStack; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class GodBowHolder { 11 | public static final Map holder = new HashMap(); 12 | public static void add(ItemStack item, GodBow bow) { 13 | holder.put(item, bow); 14 | } 15 | public static void remove(ItemStack item) { 16 | holder.remove(item); 17 | } 18 | public static GodBow get(ItemStack item) { 19 | return holder.get(item); 20 | } 21 | 22 | public static boolean contains(ItemStack item) { 23 | return holder.containsKey(item); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ScoreBoardPlugin/src/main/java/com/senko/scoreboardplugin/listener/OnPlayerQuitHandler.java: -------------------------------------------------------------------------------- 1 | package com.senko.scoreboardplugin.listener; 2 | 3 | import com.senko.scoreboardplugin.utils.ScoreboardUtil; 4 | import org.bukkit.event.EventHandler; 5 | import org.bukkit.event.EventPriority; 6 | import org.bukkit.event.Listener; 7 | 8 | /** 9 | * 玩家推出事件监听器 10 | * @author senko 11 | * @date 2022/6/14 9:46 12 | */ 13 | public class OnPlayerQuitHandler implements Listener { 14 | 15 | @EventHandler(priority = EventPriority.HIGHEST) 16 | public void onPlayerQuit(org.bukkit.event.player.PlayerQuitEvent event) { 17 | //将玩家从需要更新列表中删除 18 | ScoreboardUtil.removeUpdatePlayer(event.getPlayer()); 19 | 20 | //如果更新玩家列表为空,则停止定时任务 21 | if (ScoreboardUtil.sizeOfUpdatePlayerSet() < 1) { 22 | ScoreboardUtil.stopTaskTimer(); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /featureTest/src/main/java/com/senko/featuretest/event/CustomBowShootEvent.java: -------------------------------------------------------------------------------- 1 | package com.senko.featuretest.event; 2 | 3 | import org.bukkit.event.Cancellable; 4 | import org.bukkit.event.Event; 5 | import org.bukkit.event.HandlerList; 6 | 7 | public class CustomBowShootEvent extends Event implements Cancellable { 8 | 9 | //必须要维护的属性 10 | private static final HandlerList handlers = new HandlerList(); 11 | private boolean isCancelled; 12 | 13 | @Override 14 | public boolean isCancelled() { 15 | return this.isCancelled; 16 | } 17 | 18 | @Override 19 | public void setCancelled(boolean cancel) { 20 | this.isCancelled = cancel; 21 | } 22 | 23 | public static HandlerList getHandlerList() { 24 | return handlers; 25 | } 26 | 27 | @Override 28 | public HandlerList getHandlers() { 29 | return handlers; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /SimpleCustomEvent/src/main/java/com/senko/simplecustomevent/SimpleCustomEventPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.simplecustomevent; 2 | 3 | import com.senko.simplecustomevent.event.listener.caller.PlayerSneakingHitSheepEventCaller; 4 | import com.senko.simplecustomevent.event.listener.OnPlayerSneakingHitSheepEventListener; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | public final class SimpleCustomEventPlugin extends JavaPlugin { 9 | 10 | @Override 11 | public void onEnable() { 12 | //关于事件的补充 13 | // Bukkit.getPluginManager().registerEvents(new TestEventListener(), this); 14 | //触发自定义事件的监听器 15 | Bukkit.getPluginManager().registerEvents(new PlayerSneakingHitSheepEventCaller(), this); 16 | //自定义事件监听器 17 | Bukkit.getPluginManager().registerEvents(new OnPlayerSneakingHitSheepEventListener(), this); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /TextComponentPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: TextComponentPlugin 2 | version: '${version}' 3 | main: com.senko.textcomponentplugin.TextComponentPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 测试聊天栏文本输入以及玩家的点击事件 7 | commands: 8 | print-message: 9 | description: 展示ChatColor的两种常用用法 10 | usage: /print-message [1 | 2] 11 | print-title: 12 | description: 在玩家的屏幕中心展示一个标题和一个副标题 13 | usage: /print-title 14 | print-text-component: 15 | description: 展示TextComponent的基本使用 16 | usage: /print-text-component 17 | print-component-builder: 18 | description: 展示ComponentBuilder的基本使用 19 | usage: /print-component-builder 20 | print-boss-bar: 21 | description: 展示BossBar的基本使用 22 | usage: /print-boss-bar 23 | print-key-bind: 24 | description: 展示玩家按键绑定信息 25 | usage: /print-key-bind 26 | print: 27 | description: 打印聊天栏文本 28 | usage: /print [1|2|...] -------------------------------------------------------------------------------- /ScoreBoardPlugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ScoreBoardPlugin 2 | version: '${project.version}' 3 | main: com.senko.scoreboardplugin.ScoreBoardPlugin 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 学习如何自定义积分板 7 | commands: 8 | simple-scoreboard: 9 | description: 简单的积分板教程 10 | # 懒得挨个写错误提示,就干脆用默认的错误提示了。在这里 §f是白色的 §6是橙色的 \n是换行符,这些都可以被正确渲染出来 11 | # 用多了后就是基操( 12 | usage: 13 | " ======================简易计分板指令提示====================== \n 14 | - §6/simple-scoreboard add \n§f 添加玩家到更新队列\n 15 | - §6/simple-scoreboard remove \n§f 将玩家从更新玩家队列中移除\n 16 | - §6/simple-scoreboard start \n§f 开启定时任务\n 17 | - §6/simple-scoreboard stop \n§f 关闭定时任务\n 18 | - §6/simple-scoreboard reset [all] \n§f 重置更新玩家队列\n 19 | - §6/simple-scoreboard size \n§f 查看更新玩家队列大小\n 20 | - §6/simple-scoreboard switch [simple | no-flickering] \n§f 切换计分板的生成策略\n" -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/java/com/senko/customeventplugin/utils/InventoryUtils.java: -------------------------------------------------------------------------------- 1 | package com.senko.customeventplugin.utils; 2 | 3 | import org.bukkit.inventory.Inventory; 4 | import org.bukkit.inventory.ItemStack; 5 | 6 | /** 7 | * 管理玩家背包的工具类 8 | */ 9 | public class InventoryUtils { 10 | /** 11 | * 向容器发送物品 12 | * @param inventory 容器 13 | * @return 是否成功 14 | */ 15 | public static boolean add(Inventory inventory, ItemStack itemStack) { 16 | if (inventory == null) { 17 | //容器为空 18 | return false; 19 | } else { 20 | //容器不为空 21 | if (inventory.firstEmpty() == -1) { 22 | //容器满了 23 | return false; 24 | } else { 25 | //容器未满 26 | inventory.setItem(inventory.firstEmpty(), itemStack); 27 | } 28 | return true; 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /ParticleShootPlugin/src/main/java/github/shinyoki/particleshootplugin/factory/BulletFactory.java: -------------------------------------------------------------------------------- 1 | package github.shinyoki.particleshootplugin.factory; 2 | 3 | import org.bukkit.Material; 4 | import org.bukkit.inventory.ItemStack; 5 | import org.bukkit.inventory.meta.ItemMeta; 6 | 7 | public class BulletFactory { 8 | /** 9 | * 获取子弹 10 | */ 11 | public static ItemStack getBullet() { 12 | ItemStack itemStack = new ItemStack(Material.ARROW, 16); 13 | ItemMeta itemMeta = itemStack.getItemMeta(); 14 | itemMeta.setDisplayName("子弹"); 15 | itemStack.setItemMeta(itemMeta); 16 | 17 | return itemStack; 18 | } 19 | 20 | /** 21 | * 检查是否是子弹 22 | */ 23 | public static boolean isBullet(ItemStack itemHold) { 24 | return itemHold != null && 25 | itemHold.getType() == Material.ARROW && 26 | itemHold.getItemMeta() != null && 27 | itemHold.getItemMeta().getDisplayName().equals("子弹"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /PluginWithDatabase/src/main/java/com/senko/pluginwithdatabase/mapper/PlayerMapper.java: -------------------------------------------------------------------------------- 1 | package com.senko.pluginwithdatabase.mapper; 2 | 3 | 4 | import com.senko.pluginwithdatabase.entity.PlayerPO; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | /** 8 | * 玩家Mapper 9 | * @author senko 10 | * @date 2022/6/20 19:05 11 | */ 12 | public interface PlayerMapper { 13 | 14 | /** 15 | * 获取玩家PO 16 | * @param playerName 玩家名 17 | */ 18 | PlayerPO selectOneByName(String playerName); 19 | 20 | /** 21 | * 插入玩家PO 22 | * @param player 玩家PO 23 | * @return 插入的行数 24 | */ 25 | int insertPlayer(PlayerPO player); 26 | 27 | /** 28 | * 更新玩家PO 29 | * @param player 玩家PO 30 | * @return 更新的行数 31 | */ 32 | int updatePlayerById(PlayerPO player); 33 | 34 | /** 35 | * 删除玩家PO 36 | * @param playerName 玩家名 37 | * @return 成功删除的数量 38 | */ 39 | int deletePlayer(@Param("name") String playerName); 40 | } 41 | -------------------------------------------------------------------------------- /ParticleShootPlugin/src/main/java/github/shinyoki/particleshootplugin/ParticleShootPlugin.java: -------------------------------------------------------------------------------- 1 | package github.shinyoki.particleshootplugin; 2 | 3 | import github.shinyoki.particleshootplugin.listener.cmd.GetBulletCmd; 4 | import github.shinyoki.particleshootplugin.listener.event.BulletShootEventListener; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | import java.util.Optional; 9 | 10 | 11 | public final class ParticleShootPlugin extends JavaPlugin { 12 | 13 | 14 | private static ParticleShootPlugin instance; 15 | public static ParticleShootPlugin getInstance() { 16 | return instance; 17 | } 18 | 19 | @Override 20 | public void onEnable() { 21 | instance = this; 22 | // 指令 23 | Optional.ofNullable(getCommand("getbullet")) 24 | .ifPresent(command -> command.setExecutor(new GetBulletCmd())); 25 | 26 | // 事件 27 | Bukkit.getPluginManager() 28 | .registerEvents(new BulletShootEventListener(), this); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Command/src/main/java/com/senko/command/commands/subCmdImpl/GodMode.java: -------------------------------------------------------------------------------- 1 | package com.senko.command.commands.subCmdImpl; 2 | 3 | import com.senko.command.commands.ICommand; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.command.CommandSender; 6 | import org.bukkit.entity.Player; 7 | 8 | public class GodMode extends ICommand { 9 | public GodMode() { 10 | super("godMode","","启用上帝模式"); 11 | } 12 | 13 | @Override 14 | public boolean onCommand(CommandSender sender, String[] args) { 15 | if (sender instanceof Player) { 16 | Player player = (Player) sender; 17 | player.setInvulnerable(!player.isInvulnerable()); 18 | player.sendMessage("Boom, 现在你" + (player.isInvulnerable() ? ChatColor.BLACK+"是" : ChatColor.YELLOW+"不是") + ChatColor.WHITE + "上帝模式了!"); 19 | return true; 20 | } 21 | sender.sendMessage(ChatColor.YELLOW+"只有玩家才能启用该功能"); 22 | return false; 23 | } 24 | 25 | @Override 26 | public String permission() { 27 | return "tc.godMode"; 28 | } 29 | } -------------------------------------------------------------------------------- /Configuration/src/main/java/com/senko/configuration/listener/LoginEventListener.java: -------------------------------------------------------------------------------- 1 | package com.senko.configuration.listener; 2 | 3 | import com.senko.configuration.Configuration; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.Location; 6 | import org.bukkit.configuration.file.FileConfiguration; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.event.EventHandler; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.player.PlayerJoinEvent; 11 | 12 | public class LoginEventListener implements Listener { 13 | @EventHandler 14 | public void onLogin(PlayerJoinEvent event) { 15 | Player player = event.getPlayer(); 16 | FileConfiguration config = Configuration.getPlugin().getConfig(); 17 | 18 | Location location = config.getLocation(player.getName() + ".location"); 19 | if (location != null) { 20 | Bukkit.getServer().broadcastMessage("找到了"); 21 | player.teleport(location); 22 | }else { 23 | Bukkit.getServer().broadcastMessage("没找到"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /InventoryPlugin/src/main/java/com/senko/inventoryplugin/holder/GlobalInventory.java: -------------------------------------------------------------------------------- 1 | package com.senko.inventoryplugin.holder; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.inventory.Inventory; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class GlobalInventory { 10 | private static Map inventories = new HashMap<>(); 11 | 12 | public static boolean contains(String playerName) { 13 | return inventories.containsKey(playerName); 14 | } 15 | 16 | public static Inventory getInventory(String playerName) { 17 | 18 | if (!contains(playerName)) { 19 | //没有注册过 20 | return null; 21 | } else { 22 | //存在 23 | return inventories.get(playerName); 24 | } 25 | } 26 | 27 | public static void addToInventories(String playerName, Inventory inventory) { 28 | inventories.put(playerName, inventory); 29 | } 30 | 31 | public static Inventory inventoryInit(int capacity) { 32 | return Bukkit.createInventory(null, capacity); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /TabExecutorPlugin/src/main/java/com/senko/tabexecutorplugin/commands/impl/GodModeCmd.java: -------------------------------------------------------------------------------- 1 | package com.senko.tabexecutorplugin.commands.impl; 2 | 3 | import com.senko.tabexecutorplugin.commands.ICommand; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.command.CommandSender; 6 | import org.bukkit.entity.Player; 7 | 8 | public class GodModeCmd extends ICommand { 9 | public GodModeCmd() { 10 | super("godMode","","启用上帝模式"); 11 | } 12 | 13 | @Override 14 | public boolean onCommand(CommandSender sender, String[] args) { 15 | if (sender instanceof Player) { 16 | Player player = (Player) sender; 17 | player.setInvulnerable(!player.isInvulnerable()); 18 | player.sendMessage("Boom, 现在你" + (player.isInvulnerable() ? ChatColor.BLACK+"是" : ChatColor.YELLOW+"不是") + ChatColor.WHITE + "上帝模式了!"); 19 | return true; 20 | } 21 | sender.sendMessage(ChatColor.YELLOW+"只有玩家才能启用该功能"); 22 | return false; 23 | } 24 | 25 | @Override 26 | public String permission() { 27 | return "senko.godMode"; 28 | } 29 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | code-learning 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | CustomRecipe 13 | CustomEventPlugin 14 | 15 | 16 | 17 | 8.0.16 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.spigotmc 25 | spigot-api 26 | 1.18-R0.1-SNAPSHOT 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ConversationPlugin/src/main/java/com/senko/conversationplugin/ConversationPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.conversationplugin; 2 | 3 | import com.senko.conversationplugin.conversation.MyDefaultConversationFactory; 4 | import org.bukkit.command.Command; 5 | import org.bukkit.command.CommandSender; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.plugin.java.JavaPlugin; 8 | 9 | public final class ConversationPlugin extends JavaPlugin { 10 | 11 | private static ConversationPlugin plugin; 12 | 13 | public static ConversationPlugin getPlugin() { 14 | return plugin; 15 | } 16 | 17 | @Override 18 | public void onEnable() { 19 | plugin = this; 20 | getCommand("start").setExecutor(this); 21 | } 22 | 23 | @Override 24 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 25 | if (sender instanceof Player) { 26 | 27 | Player me = (Player) sender; 28 | MyDefaultConversationFactory.start(me); 29 | return true; 30 | } 31 | sender.sendMessage("只有玩家才嫩使用该指令!"); 32 | return true; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /PluginWithDatabase/src/main/java/com/senko/pluginwithdatabase/mapper/PlayerMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 18 | insert into player(name, uuid) values(#{name}, #{uuid}) 19 | 20 | 21 | 22 | 23 | UPDATE player SET uuid = #{uuid}, money = #{money} 24 | WHERE name = #{name} 25 | 26 | 27 | 28 | 29 | DELETE FROM player 30 | WHERE name = #{name} 31 | 32 | -------------------------------------------------------------------------------- /ScoreBoardPlugin/src/main/java/com/senko/scoreboardplugin/ScoreBoardPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.scoreboardplugin; 2 | 3 | import com.senko.scoreboardplugin.executor.SimpleScoreboardExecutor; 4 | import com.senko.scoreboardplugin.listener.OnPlayerQuitHandler; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.plugin.java.JavaPlugin; 10 | 11 | public final class ScoreBoardPlugin extends JavaPlugin { 12 | private static ScoreBoardPlugin instance; 13 | 14 | /** 15 | * 获取插件实例 16 | * @return 插件实例 17 | */ 18 | public static ScoreBoardPlugin getInstance() { 19 | return instance; 20 | } 21 | 22 | @Override 23 | public void onEnable() { 24 | instance = this; //初始化插件实例 25 | getCommand("simple-scoreboard").setExecutor(new SimpleScoreboardExecutor()); //注册命令 26 | } 27 | 28 | @Override 29 | public void onDisable() { 30 | //停止掉当前插件所生成的任务 31 | Bukkit.getScheduler().cancelTasks(this); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /ConversationPlugin/src/main/java/com/senko/conversationplugin/conversation/prefix/MyPromptPrefix.java: -------------------------------------------------------------------------------- 1 | package com.senko.conversationplugin.conversation.prefix; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.conversations.ConversationContext; 5 | import org.bukkit.conversations.ConversationPrefix; 6 | import org.bukkit.conversations.NullConversationPrefix; 7 | 8 | import java.util.Map; 9 | 10 | /** 11 | * 自定义问答的 前缀 12 | * @author senko 13 | * @date 2022/7/7 10:39 14 | */ 15 | public class MyPromptPrefix implements ConversationPrefix { 16 | @Override 17 | public String getPrefix(ConversationContext context) { 18 | //从上下文中获取信息集合 19 | Map infoMap = context.getAllSessionData(); 20 | 21 | if (!infoMap.containsKey("animal")) { 22 | //还不包含 animal时,肯定是第一个问题 23 | return ChatColor.GREEN + "问题一:" + ChatColor.WHITE; 24 | }else if (!infoMap.containsKey("number")) { 25 | return ChatColor.GREEN + "问题二:" + ChatColor.WHITE; 26 | } else if (!infoMap.containsKey("player")) { 27 | return ChatColor.GREEN + "问题三:" + ChatColor.WHITE; 28 | } 29 | 30 | return ""; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Command/src/main/java/com/senko/command/commands/subCmdImpl/Help.java: -------------------------------------------------------------------------------- 1 | package com.senko.command.commands.subCmdImpl; 2 | 3 | import com.senko.command.commands.ICommand; 4 | import com.senko.command.commands.handler.CommandHandler; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class Help extends ICommand { 8 | 9 | public Help() { 10 | super("help","","获取帮助信息"); 11 | } 12 | 13 | @Override 14 | public boolean onCommand(CommandSender sender, java.lang.String[] args) { 15 | StringBuilder str = new StringBuilder(); 16 | str.append("------- TestCommand Plugin Command List -------\n"); 17 | 18 | /** 19 | * 从父类的属性 ComandHandler 中获取到所维护的commands列表,并根据权限筛选掉不符合的指令。 20 | */ 21 | for (ICommand cmd : CommandHandler.getInstance().getCommands().values()) { 22 | if (sender.hasPermission(cmd.permission())) { 23 | //StringBuild使用append来实现字符串拼接可以提升效率 24 | str.append("/tc ").append(cmd.showUsage()).append('\n'); 25 | } 26 | } 27 | 28 | sender.sendMessage(str.toString()); 29 | return true; 30 | } 31 | 32 | @Override 33 | public String permission() { 34 | return "tc.help"; 35 | } 36 | } -------------------------------------------------------------------------------- /Command/src/main/java/com/senko/command/commands/executors/GodCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.senko.command.commands.executors; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.command.Command; 5 | import org.bukkit.command.CommandExecutor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.entity.Player; 8 | 9 | public class GodCommandExecutor implements CommandExecutor { 10 | 11 | @Override 12 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 13 | if (label.equalsIgnoreCase("god")) { 14 | //如果是玩家 15 | if (sender instanceof Player) { 16 | Player player = (Player) sender; 17 | //切换玩家的 无伤状态 18 | player.setInvulnerable(!player.isInvulnerable()); 19 | String tempMessage = (player.isInvulnerable() ? ChatColor.GREEN + "是" : ChatColor.RED + "不是"); 20 | player.sendMessage(ChatColor.WHITE + "现在你就" + tempMessage + ChatColor.WHITE + "上帝模式了。"); 21 | 22 | return true; 23 | }else { 24 | sender.sendMessage(ChatColor.YELLOW + "只有玩家才能使用该指令!"); 25 | return true; 26 | } 27 | } 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /PotionPlugin/src/main/java/com/senko/potion/PotionPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.potion; 2 | 3 | import com.senko.potion.executor.ApplyEffect; 4 | import com.senko.potion.executor.GetPotion; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.configuration.file.YamlConfiguration; 10 | import org.bukkit.entity.Player; 11 | import org.bukkit.plugin.java.JavaPlugin; 12 | import org.bukkit.potion.*; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | import java.util.Optional; 17 | 18 | 19 | public final class PotionPlugin extends JavaPlugin { 20 | 21 | @Override 22 | public void onEnable() { 23 | setCommand("potion", new ApplyEffect()); 24 | setCommand("get-potion", new GetPotion()); 25 | } 26 | 27 | protected void setCommand(String cmdName, CommandExecutor executor) { 28 | Optional.ofNullable(getCommand(cmdName)) 29 | .orElseThrow(() -> { 30 | getLogger().severe("指令" + cmdName + "不存在!"); 31 | return new RuntimeException("指令" + cmdName + "不存在!"); 32 | }) 33 | .setExecutor(executor); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /TabExecutorPlugin/src/main/java/com/senko/tabexecutorplugin/commands/impl/HelpCmd.java: -------------------------------------------------------------------------------- 1 | package com.senko.tabexecutorplugin.commands.impl; 2 | 3 | 4 | import com.senko.tabexecutorplugin.commands.ICommand; 5 | import com.senko.tabexecutorplugin.executor.CommandHandler; 6 | import org.bukkit.command.CommandSender; 7 | 8 | public class HelpCmd extends ICommand { 9 | 10 | public HelpCmd() { 11 | super("help","","获取帮助信息"); 12 | } 13 | 14 | @Override 15 | public boolean onCommand(CommandSender sender, String[] args) { 16 | StringBuilder str = new StringBuilder(); 17 | str.append("------- TestCommand Plugin Command List -------\n"); 18 | 19 | /** 20 | * 从父类的属性 ComandHandler 中获取到所维护的commands列表,并根据权限筛选掉不符合的指令。 21 | */ 22 | for (ICommand cmd : CommandHandler.getInstance().getCommands().values()) { 23 | if (sender.hasPermission(cmd.permission())) { 24 | //StringBuild使用append来实现字符串拼接可以提升效率 25 | str.append("/sub ").append(cmd.showUsage()).append('\n'); 26 | } 27 | } 28 | 29 | sender.sendMessage(str.toString()); 30 | return true; 31 | } 32 | 33 | @Override 34 | public String permission() { 35 | return "senko.help"; 36 | } 37 | } -------------------------------------------------------------------------------- /Command/src/main/java/com/senko/command/commands/ICommand.java: -------------------------------------------------------------------------------- 1 | package com.senko.command.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.command.CommandSender; 5 | 6 | public abstract class ICommand { 7 | 8 | //private CommandHandler handler; 9 | 10 | private String cmdName; 11 | private String params; 12 | private String info; 13 | 14 | public ICommand(String cmdName) { 15 | this.cmdName = cmdName; 16 | } 17 | 18 | public ICommand(String cmdName, String params,String usage) { 19 | this.cmdName = cmdName; 20 | this.info = usage; 21 | this.params = params; 22 | } 23 | 24 | public String getCmdName() { 25 | return cmdName; 26 | } 27 | 28 | /* 29 | public void setHandler(CommandHandler handler) { 30 | this.handler = handler; 31 | } 32 | */ 33 | 34 | public String showUsage() { 35 | return ChatColor.AQUA + getCmdName() + " §r" + cmdName + " "+ params + " -- " + info; 36 | } 37 | /** 38 | * 指令内容 39 | * @param sender 40 | * @param args 41 | * @return 42 | */ 43 | public abstract boolean onCommand(CommandSender sender, String[] args); 44 | 45 | /** 46 | * 指令权限 47 | * @return 48 | */ 49 | public abstract String permission(); 50 | } -------------------------------------------------------------------------------- /EasySqlTutorialPlugin/test.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : 本地mysql 5 | Source Server Type : MySQL 6 | Source Server Version : 80029 7 | Source Host : localhost:3306 8 | Source Schema : test 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80029 12 | File Encoding : 65001 13 | 14 | Date: 21/06/2022 17:49:24 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for player 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `player`; 24 | CREATE TABLE `player` ( 25 | `id` int NOT NULL AUTO_INCREMENT, 26 | `name` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '玩家名', 27 | `uuid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'UUID', 28 | `money` int NULL DEFAULT 0, 29 | PRIMARY KEY (`id`) USING BTREE, 30 | UNIQUE INDEX `player_name_uindex`(`name`) USING BTREE 31 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 32 | 33 | -- ---------------------------- 34 | -- Records of player 35 | -- ---------------------------- 36 | INSERT INTO `player` VALUES (1, 'test', 'test', 100); 37 | 38 | SET FOREIGN_KEY_CHECKS = 1; 39 | -------------------------------------------------------------------------------- /PluginWithDatabase/test.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat MySQL Data Transfer 3 | 4 | Source Server : 本地mysql 5 | Source Server Type : MySQL 6 | Source Server Version : 80029 7 | Source Host : localhost:3306 8 | Source Schema : test 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80029 12 | File Encoding : 65001 13 | 14 | Date: 21/06/2022 17:49:24 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for player 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `player`; 24 | CREATE TABLE `player` ( 25 | `id` int NOT NULL AUTO_INCREMENT, 26 | `name` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '玩家名', 27 | `uuid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT 'UUID', 28 | `money` int NULL DEFAULT 0, 29 | PRIMARY KEY (`id`) USING BTREE, 30 | UNIQUE INDEX `player_name_uindex`(`name`) USING BTREE 31 | ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 32 | 33 | -- ---------------------------- 34 | -- Records of player 35 | -- ---------------------------- 36 | INSERT INTO `player` VALUES (1, 'test', 'test', 100); 37 | 38 | SET FOREIGN_KEY_CHECKS = 1; 39 | -------------------------------------------------------------------------------- /TextComponentPlugin/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group = 'com.senko' 6 | version = '1.0-SNAPSHOT' 7 | 8 | repositories { 9 | mavenCentral() 10 | maven { 11 | name = 'spigotmc-repo' 12 | url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' 13 | } 14 | maven { 15 | name = 'sonatype' 16 | url = 'https://oss.sonatype.org/content/groups/public/' 17 | } 18 | } 19 | 20 | dependencies { 21 | compileOnly 'org.spigotmc:spigot-api:1.18-R0.1-SNAPSHOT' 22 | } 23 | 24 | def targetJavaVersion = 17 25 | java { 26 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 27 | sourceCompatibility = javaVersion 28 | targetCompatibility = javaVersion 29 | if (JavaVersion.current() < javaVersion) { 30 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 31 | } 32 | } 33 | 34 | tasks.withType(JavaCompile).configureEach { 35 | if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { 36 | options.release = targetJavaVersion 37 | } 38 | options.encoding("UTF-8") 39 | } 40 | 41 | processResources { 42 | def props = [version: version] 43 | inputs.properties props 44 | filteringCharset 'UTF-8' 45 | filesMatching('plugin.yml') { 46 | expand props 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /TabExecutorPlugin/src/main/java/com/senko/tabexecutorplugin/commands/ICommand.java: -------------------------------------------------------------------------------- 1 | package com.senko.tabexecutorplugin.commands; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.command.CommandSender; 5 | 6 | public abstract class ICommand { 7 | 8 | /** 9 | * 子指令名 10 | */ 11 | private String cmdName; 12 | 13 | /** 14 | * 子指令参数 15 | */ 16 | private String params; 17 | 18 | /** 19 | * 子指令描述 20 | */ 21 | private String info; 22 | 23 | public ICommand(String cmdName) { 24 | this.cmdName = cmdName; 25 | } 26 | 27 | public ICommand(String cmdName, String params,String usage) { 28 | this.cmdName = cmdName; 29 | this.info = usage; 30 | this.params = params; 31 | } 32 | 33 | public String getCmdName() { 34 | return cmdName; 35 | } 36 | 37 | 38 | public String showUsage() { 39 | return ChatColor.AQUA + getCmdName() + " §r" + cmdName + " "+ params + " -- " + info; 40 | } 41 | /** 42 | * 指令内容 43 | * @param sender 发送者 44 | * @param args 参数 45 | * @return 是否执行成功 46 | */ 47 | public abstract boolean onCommand(CommandSender sender, String[] args); 48 | 49 | /** 50 | * 指令权限 51 | * @return 权限 52 | */ 53 | public abstract String permission(); 54 | } -------------------------------------------------------------------------------- /ParticleShootPlugin/src/main/java/github/shinyoki/particleshootplugin/listener/cmd/GetBulletCmd.java: -------------------------------------------------------------------------------- 1 | package github.shinyoki.particleshootplugin.listener.cmd; 2 | 3 | import github.shinyoki.particleshootplugin.factory.BulletFactory; 4 | import org.bukkit.Sound; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.bukkit.inventory.PlayerInventory; 11 | 12 | public class GetBulletCmd implements CommandExecutor { 13 | @Override 14 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 15 | if (sender instanceof Player) { 16 | Player player = (Player) sender; 17 | 18 | // 添加物品 19 | PlayerInventory inventory = player.getInventory(); 20 | if (inventory.firstEmpty() != -1) { 21 | ItemStack bullet = BulletFactory.getBullet(); 22 | inventory.addItem(bullet); 23 | 24 | player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 1, 1); 25 | player.sendMessage("获得子弹" + bullet.getAmount() + "发"); 26 | } else { 27 | player.sendMessage("背包已满"); 28 | } 29 | } else { 30 | sender.sendMessage("该指令只能由玩家执行"); 31 | } 32 | return true; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Command/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Command 2 | version: '${project.version}' 3 | main: com.senko.command.Command 4 | api-version: 1.18 5 | authors: [ senko ] 6 | description: 玩家指令发送 7 | 8 | 9 | #指令&权限: 10 | #指令的触发需要CommandSender拥有对应的Permission权限 11 | #如果在定义指令时并没有显式声明,则默认需要OP权限。 12 | 13 | #指令声明 14 | commands: 15 | #自定义指令:不能以 / 开头 16 | suicide: 17 | #指令描述 18 | description: 自杀 19 | #错误使用提示 当CommandExecutor#onCommand函数返回false时,就会根据该key的value用来提示玩家 20 | # / 会被自动代替为 "/suicide" 21 | usage: / 22 | #指令的别名:/s1 /s2 /s3 都会被认为是/suicide指令 23 | aliases: [s1, s2, s3] 24 | #指令的权限:如果不在下面显式声明该permission,则默认为OP才能使用 25 | permission: senko.suicide 26 | #无权限提示:当CommandSender指令发送者没有足够的权限时,就会被提示该信息 27 | permission-message: 哦,你好像没有使用该指令的权限~ 28 | #自定义指令: 29 | god: 30 | description: 切换为上帝模式,让你不会受到任何伤害~ 31 | usage: / 32 | # yml文件的另一种list值表达方式 33 | aliases: 34 | - g1 35 | - g2 36 | - g3 37 | permission: senko.god 38 | permission-message: 只有OP才能使用该指令哦。 39 | tc: 40 | usage: / 41 | description: 测试Handler注册指令 42 | 43 | #Permissions权限声明 44 | permissions: 45 | #自定义权限名 46 | senko.suicide: 47 | description: 自杀权限 48 | #设置默认值,true:全部玩家都能使用,op:只能让op使用 49 | default: true 50 | senko.god: 51 | description: 成为上帝 52 | default: op 53 | tc.help: 54 | # default: true意味着所有玩家都有权限 55 | default: true 56 | tc.godMode: 57 | default: true -------------------------------------------------------------------------------- /featureTest/src/main/java/com/senko/featuretest/event/RecipeCraftEvent.java: -------------------------------------------------------------------------------- 1 | package com.senko.featuretest.event; 2 | 3 | import org.bukkit.NamespacedKey; 4 | import org.bukkit.event.EventHandler; 5 | import org.bukkit.event.Listener; 6 | import org.bukkit.event.inventory.CraftItemEvent; 7 | import org.bukkit.inventory.*; 8 | 9 | /** 10 | * @author senko 11 | * @date 2022/8/8 14:54 12 | */ 13 | public class RecipeCraftEvent implements Listener { 14 | 15 | @EventHandler 16 | public void onCraft(CraftItemEvent event) { 17 | // 通过事件获取配方 18 | Recipe recipe = event.getRecipe(); 19 | if (recipe instanceof ShapedRecipe) { 20 | // 如果是ShapedRecipe,则一定是工作台里的配方 21 | ShapedRecipe shapedRecipe = (ShapedRecipe) recipe; 22 | // 转型并获取命名空间 23 | NamespacedKey key = shapedRecipe.getKey(); 24 | 25 | String namespace = key.getNamespace(); // 如果是原版配方,则为minecraft, 如果是自定义配方,则为自定义插件的名称 26 | String keyName = key.getKey(); // 如果是原版配方,则为物品的名字,如果是自定义配方,则为自定义配方的名字 27 | event.getWhoClicked().sendMessage("配方来源于:" + namespace); 28 | event.getWhoClicked().sendMessage("配方名称:" + keyName); 29 | 30 | if (namespace.equals("minecraft")) { 31 | CraftingInventory inventory = event.getInventory(); 32 | event.setCancelled(true); 33 | inventory.setResult(null); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Command/README.md: -------------------------------------------------------------------------------- 1 | # Command 2 | - Command指令需要在`resources/config.yml`配置文件的`Commands属性`中声明,详情请看[这里](https://www.spigotmc.org/wiki/plugin-yml/#commands)。 3 | - 接着我们需要实现`CommandExecutor`接口,复写里面的`onCommand()`函数。 4 | - 最后在`Plugin`里通过`getCommand("commandName").setExecutor(urCommandsExecutor))`来完成指令绑定。 5 | > `sender`可能是`Player`也可能是命令方块或控制台,请注意区分。 6 | 7 | # CommandHandler 8 | 9 | 假设我定义了一个指令,`label`为 senko,我想以后设计很多子指令例如 help,GodMode,那么只需要输入 `/senko [help | GodMode]`即可,这时我们当然可以直接定义多个`CommandExecutor`实现类,然后到Plugin里与`plugin.yml`中声明的指令挨个绑定,但这样非常麻烦。 10 | 11 | 有没有一种方法,只需要定义一个label为senko,然后脱离配置文件来进行声明呢? 12 | 13 | 14 | 15 | 这里我们可以使用Handler设计模式。 16 | 17 | > - `ICommand`抽象类 18 | > 19 | > - 属性: 20 | > - CmdName:指令名称 21 | > - Params:指令参数 22 | > - Usage:指令介绍/教程 23 | > 24 | > - 函数: 25 | > - `onCommand()`:被Handler统一调用 26 | > - `permission()`:指令权限 27 | > 28 | > - `ICommand`的实现类 29 | > 30 | > - 函数: 31 | > - `constructor()`:通过super()将指令信息初始化。 32 | > - `onCommand()`实现 33 | > - `permission()`实现 34 | > 35 | > - `CommandHandler`:本身就是`TabExecutor`的实现类 36 | > 37 | > - 属性 38 | > - 自己的单例 39 | > - 维护的ICommand指令 40 | > - 函数 41 | > - `showHelp()` 42 | > - `onCommand()`:根据args判断应调用哪条指令 43 | > - `onTabComplete()`:根据args判断应回应哪条指令作为返回值 44 | > - `init()`:初始化所维护的ICommand指令 45 | 46 | 47 | 48 | 现在我们只需要在`plugin.yml`中声明一个senko指令,然后将写好的ICommand子类,初始化到CommandHandler所维护的map中,在Plugin里通过`getCommand("senko").setExecutor(new CommandHandler())`。即可实现,多条子指令的一键声明绑定了。 49 | -------------------------------------------------------------------------------- /SimpleCustomEvent/src/main/java/com/senko/simplecustomevent/event/listener/OnPlayerSneakingHitSheepEventListener.java: -------------------------------------------------------------------------------- 1 | package com.senko.simplecustomevent.event.listener; 2 | 3 | import com.senko.simplecustomevent.event.PlayerSneakingHitSheepEvent; 4 | import org.bukkit.DyeColor; 5 | import org.bukkit.Location; 6 | import org.bukkit.Sound; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.entity.Sheep; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.Listener; 11 | import org.bukkit.util.Vector; 12 | 13 | import java.util.Random; 14 | 15 | /** 16 | *
17 |  * 事件监听器
18 |  *
19 |  * 当玩家在潜行时击打小羊,小样就会垂直起飞、变换羊毛色彩、并给玩家一个音效提示.
20 |  * 确实是在模仿Hyoixel,但是我这没有给羊监听摔落伤害事件(
21 |  * 
22 | * @author senko 23 | * @date 2022/7/2 19:54 24 | */ 25 | public class OnPlayerSneakingHitSheepEventListener implements Listener { 26 | 27 | @EventHandler 28 | public void onPlayerSneakingHitSheepEventListener(PlayerSneakingHitSheepEvent event) { 29 | Player player = event.getPlayer(); 30 | Sheep sheep = event.getSheep(); 31 | Random random = new Random(); 32 | int next = random.nextInt(DyeColor.values().length); // [0, length) 左闭右开的int 33 | 34 | //随机颜色 35 | sheep.setColor(DyeColor.values()[next]); 36 | //垂直起飞:施加Velocity速度,速度矢量为y轴方向一个单位 37 | sheep.setVelocity(new Vector(0, 1, 0)); 38 | //音效:玩家#playSound和word#playSound是有区别的,一个是只给该玩家发送音效封包,另一个是给全体玩家发送音效封包 39 | player.playSound(player.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F); 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /BlockPlugin/src/main/java/com/senko/blockplugin/examples/blockdata/ApplyPhysicsExample.java: -------------------------------------------------------------------------------- 1 | package com.senko.blockplugin.examples.blockdata; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Material; 5 | import org.bukkit.block.Block; 6 | import org.bukkit.block.data.BlockData; 7 | import org.bukkit.block.data.type.Chest; 8 | import org.bukkit.entity.Player; 9 | 10 | /** 11 | * 12 | * @author senko 13 | * @date 2022/7/15 10:20 14 | */ 15 | public class ApplyPhysicsExample { 16 | public void doSetBlockData(Block clickedBlock, Player player) { 17 | 18 | // 如果是箱子就将箱子删除 19 | if (clickedBlock.getType() == Material.CHEST){ 20 | 21 | BlockData airBlockData = Bukkit.createBlockData(Material.AIR); 22 | player.sendMessage("现在我要删除该箱子,且不会引发周围的方块更新!"); 23 | 24 | /** 25 | * 第二个参数applyPhysics = false,意味着修改方块信息不会引发周围的方块更新。 26 | * 因为有些情况,插件需要大面积的修改方块,这时候如果选择触发方块更新,可能会出现意想不到的效果 27 | * (腐竹,你也不想让服务器因为巨浪的方块更新计算导致暴毙吧)。 28 | * 比如著名的/co i指令,如果管理员选择对玩家进行事务回滚操作。最后回滚完的区域可能留下了 29 | * 浮空火把,又或者是we里对地形的改造。都选择了不触发方块更新。 30 | * 31 | * {@link Block#setType(Material, boolean)} 也可以修改方块的类型,也可以设置是否触发方块更新。 32 | * 但是原有的方块状态BlockState和方块信息BlockData会被覆盖为默认的,和这里的Bukkit.createBlockData(Material.AIR)有点类似。 33 | */ 34 | clickedBlock.setBlockData(airBlockData, true); // 关闭方块更新 的修改 35 | // clickedBlock.setBlockData(airBlockData); // 默认启用方块更新 的修改 36 | 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /InventoryPlugin/src/main/java/com/senko/inventoryplugin/listener/CantGetFromChestListener.java: -------------------------------------------------------------------------------- 1 | package com.senko.inventoryplugin.listener; 2 | 3 | 4 | import com.senko.inventoryplugin.holder.GlobalInventory; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.Material; 7 | import org.bukkit.entity.HumanEntity; 8 | import org.bukkit.event.EventHandler; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.inventory.InventoryClickEvent; 11 | import org.bukkit.inventory.Inventory; 12 | import org.bukkit.inventory.ItemStack; 13 | import org.bukkit.inventory.meta.ItemMeta; 14 | 15 | public class CantGetFromChestListener implements Listener { 16 | @EventHandler 17 | public void onGet(InventoryClickEvent event) { 18 | Inventory inventory = event.getInventory(); 19 | HumanEntity whoClicked = event.getWhoClicked(); 20 | if (GlobalInventory.contains(whoClicked.getName())) { 21 | Inventory remoteChest = GlobalInventory.getInventory(whoClicked.getName()); 22 | if (remoteChest.equals(inventory)) { 23 | 24 | ItemStack itemStack = new ItemStack(Material.FEATHER, 1); 25 | ItemMeta itemMeta = itemStack.getItemMeta(); 26 | itemMeta.setDisplayName(ChatColor.RED+"小鸡毛"); 27 | if (event.getCurrentItem().getItemMeta().getDisplayName().equals(ChatColor.RED + "小鸡毛")) { 28 | whoClicked.sendMessage(ChatColor.RED + "哎,不让你拿~"); 29 | event.setCancelled(true); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ConversationPlugin/src/main/java/com/senko/conversationplugin/conversation/promts/FinalFriendMessagePrompt.java: -------------------------------------------------------------------------------- 1 | package com.senko.conversationplugin.conversation.promts; 2 | 3 | import org.bukkit.Sound; 4 | import org.bukkit.command.ConsoleCommandSender; 5 | import org.bukkit.conversations.ConversationContext; 6 | import org.bukkit.conversations.MessagePrompt; 7 | import org.bukkit.conversations.Prompt; 8 | import org.bukkit.entity.EntityType; 9 | import org.bukkit.entity.Player; 10 | 11 | /** 12 | * 友好信息提示 13 | * @author senko 14 | * @date 2022/7/7 11:09 15 | */ 16 | public class FinalFriendMessagePrompt extends MessagePrompt { 17 | @Override 18 | protected Prompt getNextPrompt(ConversationContext context) { 19 | //不需要继续提出问题了 20 | return null; 21 | } 22 | 23 | @Override 24 | public String getPromptText(ConversationContext context) { 25 | String animal = (String) context.getSessionData("animal"); 26 | Integer number = (Integer) context.getSessionData("number"); 27 | Player targetPlayer = (Player) context.getSessionData("player"); 28 | 29 | //给对应玩家生成实体 30 | for (Integer i = 0; i < number; i++) { 31 | targetPlayer.getWorld().spawnEntity(targetPlayer.getLocation(), EntityType.valueOf(animal)); 32 | } 33 | 34 | //获取问题的回复者 35 | Player forWhom = (Player) context.getForWhom(); 36 | forWhom.playSound(forWhom.getLocation(), Sound.BLOCK_ANVIL_BREAK, 1, 1); 37 | 38 | return "成功生成了 " + number + "只" + animal + "给" + targetPlayer.getName() + "!"; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /PluginWithDatabase/src/main/java/com/senko/pluginwithdatabase/entity/PlayerPO.java: -------------------------------------------------------------------------------- 1 | package com.senko.pluginwithdatabase.entity; 2 | 3 | /** 4 | * 持久层对象 玩家 5 | * @author senko 6 | * @date 2022/6/20 18:57 7 | */ 8 | public class PlayerPO { 9 | 10 | /** 11 | * 持久层PO对象, 12 | * 对于一些基本类型,建议改成包装类型, 13 | * 这样可以传入NULL值来避免出现默认值的情况。 14 | * 15 | * 后端人的必备常识( 16 | */ 17 | private Integer id; // 玩家ID 18 | private String name; // 玩家名 19 | private String uuid; // 玩家等级 20 | private Integer money; // 玩家金钱 21 | 22 | 23 | @Override 24 | public String toString() { 25 | return "PlayerPO{" + 26 | "id=" + id + 27 | ", name='" + name + '\'' + 28 | ", uuid='" + uuid + '\'' + 29 | ", money=" + money + 30 | '}'; 31 | } 32 | 33 | public Integer getId() { 34 | return id; 35 | } 36 | 37 | public void setId(Integer id) { 38 | this.id = id; 39 | } 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public void setName(String name) { 46 | this.name = name; 47 | } 48 | 49 | public String getUuid() { 50 | return uuid; 51 | } 52 | 53 | public void setUuid(String uuid) { 54 | this.uuid = uuid; 55 | } 56 | 57 | public Integer getMoney() { 58 | return money; 59 | } 60 | 61 | public void setMoney(Integer money) { 62 | this.money = money; 63 | } 64 | 65 | public PlayerPO(Integer id, String name, String uuid, Integer money) { 66 | this.id = id; 67 | this.name = name; 68 | this.uuid = uuid; 69 | this.money = money; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /ConversationPlugin/src/main/java/com/senko/conversationplugin/conversation/promts/SecondPrompt.java: -------------------------------------------------------------------------------- 1 | package com.senko.conversationplugin.conversation.promts; 2 | 3 | import com.senko.conversationplugin.ConversationPlugin; 4 | import org.bukkit.conversations.ConversationContext; 5 | import org.bukkit.conversations.NumericPrompt; 6 | import org.bukkit.conversations.Prompt; 7 | 8 | /** 9 | * 第二个问题:需要生成 X 只 XX 生物? 10 | * @author senko 11 | * @date 2022/7/7 10:31 12 | */ 13 | public class SecondPrompt extends NumericPrompt { 14 | 15 | /** 16 | * 自己重载了这个方法,用来手动校验数字是否正确 17 | * 18 | * 不是所有的方法都满足我们的需求,有需求就自己Override或写个自己的方法 19 | * @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文 20 | * @param input 玩家给出的输入 21 | * @return 校验结果,true表示校验通过,false表示校验不通过 22 | */ 23 | @Override 24 | protected boolean isNumberValid(ConversationContext context, Number input) { 25 | //如果玩家输入的数字小于等于0,则认为输入错误 26 | return input.intValue() >= 1 && input.intValue() <= 10; 27 | } 28 | 29 | /** 30 | * 输入校验成功 的后处理 31 | * @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文 32 | * @param input 校验成功的数字 33 | * @return 下一个提示对象,返回null则关闭会话 34 | */ 35 | @Override 36 | protected Prompt acceptValidatedInput(ConversationContext context, Number input) { 37 | //存储数字到上下文中 38 | context.setSessionData("number", input.intValue()); 39 | return new ThirdPrompt(ConversationPlugin.getPlugin()); 40 | } 41 | 42 | /** 43 | * 提示信息 44 | * @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文 45 | * @return 提示信息 46 | */ 47 | @Override 48 | public String getPromptText(ConversationContext context) { 49 | return "你要生成几只" + context.getSessionData("animal") + "呢?(允许数量:[1 ~ 10])"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /TextComponentPlugin/src/main/java/com/senko/textcomponentplugin/executor/PrintTitle.java: -------------------------------------------------------------------------------- 1 | package com.senko.textcomponentplugin.executor; 2 | 3 | import com.senko.textcomponentplugin.TextComponentPlugin; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.Sound; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandExecutor; 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | 12 | /** 13 | * 在玩家的屏幕中心展示一个标题和一个副标题 14 | * 15 | * @author senko 16 | * @date 2022/6/10 7:58 17 | */ 18 | public class PrintTitle implements CommandExecutor { 19 | 20 | /** 21 | * 屏幕中心区域的标题与副标题 22 | * 需要直接通过{@link org.bukkit.entity.Player#sendTitle(String, String, int, int, int)}来发送, 23 | */ 24 | @Override 25 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 26 | if (sender instanceof Player) { 27 | Player player = (Player) sender; 28 | 29 | 30 | //标题: §l§6Area Discovered 31 | StringBuilder titleBuilder = new StringBuilder() 32 | .append(ChatColor.BOLD).append(ChatColor.GOLD).append("Area Discovered"); 33 | //副标题: §eRagni 34 | StringBuilder subTitle = new StringBuilder() 35 | .append(ChatColor.YELLOW).append("Ragni"); 36 | 37 | /** 38 | * 发送标题和副标题 39 | * 参数一:标题 40 | * 参数二:副标题 41 | * 参数三:渐入花费的时间(tick单位) 42 | * 参数四:标题停留的时间(tick单位) 43 | * 参数五:渐出花费的时间(tick单位) 44 | */ 45 | player.sendTitle(titleBuilder.toString(), subTitle.toString(), 24, 60, 24); 46 | player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1,1); //模仿wynncraft的区域发现音效 47 | 48 | return true; 49 | } 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /featureTest/src/main/java/com/senko/featuretest/listener/OnEatEventListener.java: -------------------------------------------------------------------------------- 1 | package com.senko.featuretest.listener; 2 | 3 | import org.bukkit.Material; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.HandlerList; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.player.PlayerItemConsumeEvent; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.bukkit.plugin.RegisteredListener; 11 | 12 | import java.util.ArrayList; 13 | 14 | /** 15 | * 吃完食物后会触发coolDown冷却时间 16 | */ 17 | public class OnEatEventListener implements Listener { 18 | @EventHandler 19 | public void onEat(PlayerItemConsumeEvent event) { 20 | ItemStack item = event.getItem(); 21 | Material type = item.getType(); 22 | if (type.equals(Material.GOLDEN_APPLE)) { 23 | //如果是吃到的是金苹果,则触发coolDown 24 | event.getPlayer().setCooldown(Material.GOLDEN_APPLE, 20 * 5); 25 | ArrayList handlerLists = HandlerList.getHandlerLists(); 26 | Player player = event.getPlayer(); 27 | player.sendMessage("得到的HandlerList数组:" + handlerLists.size()); 28 | int i = 0; 29 | for (HandlerList handlerList : handlerLists) { 30 | 31 | player.sendMessage("得到的第"+i+"个HandlerList:==========================="); 32 | RegisteredListener[] registeredListeners = handlerList.getRegisteredListeners(); 33 | for (RegisteredListener registeredListener : registeredListeners) { 34 | player.sendMessage("所属插件:" + registeredListener.getPlugin().getName()); 35 | player.sendMessage("所属优先级:" + registeredListener.getPriority().getSlot()); 36 | player.sendMessage("监听器的类名:"+registeredListener.getListener().getClass().getName()); 37 | } 38 | i++; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /PersistentPlugin/src/main/java/com/senko/persistentplugin/data/LocalDateTimeDataTypeImpl.java: -------------------------------------------------------------------------------- 1 | package com.senko.persistentplugin.data; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import org.bukkit.persistence.PersistentDataAdapterContext; 5 | import org.bukkit.persistence.PersistentDataType; 6 | 7 | import java.time.LocalDateTime; 8 | import java.util.Objects; 9 | 10 | /** 11 | * LocalDateTime类型 12 | * @author senko 13 | * @date 2022/7/6 13:10 14 | */ 15 | public class LocalDateTimeDataTypeImpl implements PersistentDataType { 16 | 17 | /** 18 | * 获取原始类型 19 | */ 20 | @Override 21 | public Class getPrimitiveType() { 22 | return byte[].class; 23 | } 24 | 25 | /** 26 | * 获取复杂类型(就算我们要存储的对象class 27 | */ 28 | @Override 29 | public Class getComplexType() { 30 | return LocalDateTime.class; 31 | } 32 | 33 | /** 34 | * localDateTime对象序列化为byte[] 35 | * 36 | * 有一个更偷懒的方式,借用JSON工具类, 37 | * 把对象转换成JSON,再getBytes 38 | */ 39 | @Override 40 | public byte[] toPrimitive(LocalDateTime complex, PersistentDataAdapterContext context) { 41 | if (Objects.isNull(complex)) { 42 | //如果对象为空,则返回 初始化的byte数组 43 | return new byte[0]; 44 | } 45 | //如果不为空,则利用fastjson将LocalDateTime先转换成JSON,再getByte变成byte数组 46 | return JSON.toJSONBytes(complex); 47 | } 48 | 49 | /** 50 | * byte[]数组反序列化回LocalDateTime 51 | */ 52 | @Override 53 | public LocalDateTime fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) { 54 | if (Objects.isNull(primitive) || primitive.length <= 0) { 55 | //如果byte数组为空,则返回null值 56 | return null; 57 | } 58 | //不为空,则使用fastjson反序列化 59 | Object datetime = JSON.parseObject(primitive, LocalDateTime.class); 60 | return (LocalDateTime) datetime; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /TextComponentPlugin/src/main/java/com/senko/textcomponentplugin/executor/PrintKeyBind.java: -------------------------------------------------------------------------------- 1 | package com.senko.textcomponentplugin.executor; 2 | 3 | import net.md_5.bungee.api.ChatMessageType; 4 | import net.md_5.bungee.api.chat.KeybindComponent; 5 | import net.md_5.bungee.api.chat.Keybinds; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | 11 | /** 12 | * 按键绑定信息 13 | * @author senko 14 | * @date 2022/6/10 9:04 15 | */ 16 | public class PrintKeyBind implements CommandExecutor { 17 | 18 | 19 | /** 20 | * 玩家绑定的按键
21 | * Spigot可以获取到玩家的 攻击、丢弃物品等快捷键所绑定的案件是什么。
22 | * 按键key的类型可以参考 MC wiki 以及{@link Keybinds}
23 | * 服务器可以根据客户端的 ”语言和绑定的按键” 来决定应该输出哪种语言的按键名称。 24 | */ 25 | @Override 26 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 27 | if (sender instanceof Player) { 28 | Player player = (Player) sender; 29 | 30 | executeFun(player); 31 | 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | private void executeFun(Player player) { 38 | KeybindComponent key1 = new KeybindComponent(); 39 | key1.setKeybind(Keybinds.ATTACK); //攻击,默认左键 40 | key1.addExtra(":攻击键"); 41 | 42 | KeybindComponent key2 = new KeybindComponent(); 43 | key2.setKeybind("key.drop"); //丢弃物品,默认Q键------ 我自定义的是R键 英文:r 中文:r 44 | key2.addExtra(":丢弃物品"); 45 | 46 | KeybindComponent key3 = new KeybindComponent("key.playerlist"); 47 | key3.addExtra(":玩家列表"); //玩家列表,默认Tab键---- 我定义的是`键 48 | 49 | player.spigot().sendMessage(key1); 50 | player.spigot().sendMessage(key2); 51 | player.spigot().sendMessage(key3); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ConversationPlugin/src/main/java/com/senko/conversationplugin/conversation/promts/FirstPrompt.java: -------------------------------------------------------------------------------- 1 | package com.senko.conversationplugin.conversation.promts; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.conversations.ConversationContext; 5 | import org.bukkit.conversations.FixedSetPrompt; 6 | import org.bukkit.conversations.Prompt; 7 | import org.bukkit.entity.EntityType; 8 | 9 | /** 10 | * 第一个问题:选择需要生成的生物[COW, PIG, CAT, NONE] 11 | * @author senko 12 | * @date 2022/7/7 10:14 13 | */ 14 | public class FirstPrompt extends FixedSetPrompt { 15 | /** 16 | * 指出需要的 答案 17 | */ 18 | public FirstPrompt() { 19 | //传入Fixed固定的答案 20 | super(EntityType.COW.toString(), EntityType.PIG.toString(), EntityType.CAT.toString(), "NONE"); 21 | } 22 | 23 | 24 | /** 25 | * 输入正确回复 的后处理 26 | * @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文 27 | * @param input 与super()里添加的默认文本相同的input,可惜这里的input是匹配后的,如果想自己来验证,可以Override 父类的isInputValid方法 28 | * @return 下一个问题,如果return null则相当于关闭了会话 29 | */ 30 | @Override 31 | protected Prompt acceptValidatedInput(ConversationContext context, String input) { 32 | /** 33 | * 这里的input只会是 构造方法里传入的那些String, 34 | * 如果想自己来验证input是否匹配构造方法里传入的默认参数,可以Override 父类的isInputValid方法 35 | */ 36 | if (input.equalsIgnoreCase("none")) { 37 | //同等与 return null 38 | return Prompt.END_OF_CONVERSATION; 39 | } 40 | //通过context保存自定义中间信息,方便在以后的问答会话中获取 41 | context.setSessionData("animal", input); 42 | return new SecondPrompt(); 43 | } 44 | 45 | /** 46 | * 一开始给出的提示 47 | * @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文 48 | * @return 当前问题的提示 49 | */ 50 | @Override 51 | public String getPromptText(ConversationContext context) { 52 | //由于是第一个问题:我们暂时用不到context来获取什么信息 53 | return "请选择需要生成的动物:" + ChatColor.WHITE + this.formatFixedSet(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ParticleShootPlugin/src/main/java/github/shinyoki/particleshootplugin/listener/event/BulletShootEventListener.java: -------------------------------------------------------------------------------- 1 | package github.shinyoki.particleshootplugin.listener.event; 2 | 3 | import github.shinyoki.particleshootplugin.effect.BulletShootEffect; 4 | import github.shinyoki.particleshootplugin.factory.BulletFactory; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.Color; 7 | import org.bukkit.Sound; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.Listener; 11 | import org.bukkit.event.block.Action; 12 | import org.bukkit.event.player.PlayerInteractEvent; 13 | import org.bukkit.inventory.EquipmentSlot; 14 | import org.bukkit.inventory.ItemStack; 15 | 16 | public class BulletShootEventListener implements Listener { 17 | 18 | @EventHandler 19 | public void onBulletShoot(PlayerInteractEvent event) { 20 | Player player = event.getPlayer(); 21 | Action action = event.getAction(); 22 | EquipmentSlot hand = event.getHand(); 23 | if (checkMainHandAndRightClick(action, hand)) { 24 | 25 | // 检查背包里是否存在子弹 26 | ItemStack itemHold = event.getItem(); 27 | if (BulletFactory.isBullet(itemHold)) { 28 | // 扣除数量 29 | itemHold.setAmount(itemHold.getAmount() - 1); 30 | player.sendMessage("剩余子弹" + ChatColor.RED + itemHold.getAmount() + ChatColor.WHITE + "发"); 31 | 32 | player.getWorld().playSound(player.getLocation(), Sound.ENTITY_ARROW_SHOOT, 1, 1); 33 | 34 | // 发射子弹 35 | new BulletShootEffect(player) 36 | .start(); 37 | } 38 | } 39 | 40 | } 41 | 42 | /** 43 | * 检查是 主手和右键 44 | */ 45 | private boolean checkMainHandAndRightClick(Action action, EquipmentSlot hand) { 46 | return hand == EquipmentSlot.HAND && 47 | (action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /SimpleCustomEvent/src/main/java/com/senko/simplecustomevent/event/PlayerSneakingHitSheepEvent.java: -------------------------------------------------------------------------------- 1 | package com.senko.simplecustomevent.event; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.entity.Sheep; 5 | import org.bukkit.event.Cancellable; 6 | import org.bukkit.event.Event; 7 | import org.bukkit.event.HandlerList; 8 | 9 | /** 10 | *
11 |  * 自定义事件
12 |  *
13 |  * 当玩家潜行时伤害了一只羊事件。
14 |  *
15 |  * 自定义事件需要配合监听器或是在其他代码里,
16 |  * 用{@link org.bukkit.plugin.PluginManager#callEvent(Event)}手动触发该事件
17 |  * 
18 | * @author senko 19 | * @date 2022/7/2 18:48 20 | */ 21 | public class PlayerSneakingHitSheepEvent extends Event implements Cancellable { 22 | 23 | /** 24 | * 服务器中该事件的处理器集合, 25 | * 我们只需要实现{@link Event#getHandlers()}然后返回这个对象就好, 26 | * 具体的赋值会被服务端处理。 27 | */ 28 | private static final HandlerList handlers = new HandlerList(); 29 | 30 | /** 31 | * 事件状态 32 | */ 33 | private boolean isCancelled; 34 | 35 | /** 36 | * 玩家 37 | */ 38 | private Player player; 39 | 40 | /** 41 | * 羊 42 | */ 43 | private Sheep sheep; 44 | 45 | 46 | public PlayerSneakingHitSheepEvent(Player player, Sheep sheep) { 47 | this.player = player; 48 | this.sheep = sheep; 49 | } 50 | 51 | /** 52 | * 一个是需要实现的getHandlers 53 | */ 54 | @Override 55 | public HandlerList getHandlers() { 56 | return handlers; 57 | } 58 | 59 | /** 60 | * 一个是约定的getHandlerList 61 | */ 62 | public static HandlerList getHandlerList() { 63 | return handlers; 64 | } 65 | 66 | @Override 67 | public boolean isCancelled() { 68 | return isCancelled; 69 | } 70 | 71 | public boolean nonCancelled() { 72 | return isCancelled == false; 73 | } 74 | @Override 75 | public void setCancelled(boolean cancel) { 76 | this.isCancelled = cancel; 77 | } 78 | 79 | public Player getPlayer() { 80 | return player; 81 | } 82 | 83 | public Sheep getSheep() { 84 | return sheep; 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /ConversationPlugin/src/main/java/com/senko/conversationplugin/conversation/MyDefaultConversationFactory.java: -------------------------------------------------------------------------------- 1 | package com.senko.conversationplugin.conversation; 2 | 3 | import com.senko.conversationplugin.ConversationPlugin; 4 | import com.senko.conversationplugin.conversation.prefix.MyPromptPrefix; 5 | import com.senko.conversationplugin.conversation.promts.FirstPrompt; 6 | import org.bukkit.conversations.Conversable; 7 | import org.bukkit.conversations.ConversationFactory; 8 | 9 | /** 10 | * 创建并开启自定义会话 11 | * 12 | * @author senko 13 | * @date 2022/7/7 9:49 14 | */ 15 | public class MyDefaultConversationFactory { 16 | 17 | /** 18 | * 问答会话 工厂 19 | */ 20 | private static ConversationFactory conversationFactory; 21 | 22 | /** 23 | * 初始化问答会话工厂 24 | */ 25 | static { 26 | conversationFactory = new ConversationFactory(ConversationPlugin.getPlugin()) 27 | //factory对象的playerOnlyMessage属性如果非空, 则会阻止控制台发起Conversation会话 28 | .thatExcludesNonPlayersWithMessage("控制台你这次别参与了捏~") 29 | //会话途中退出 用的语句 30 | .withEscapeSequence("quit") 31 | //10秒后没有得到 (玩家|控制台)的回复,则关闭此次会话 32 | .withTimeout(10) 33 | //第一次显示给(玩家|控制台)的问题(Prompt的翻译是提示,我感觉叫成问题更为合适) 34 | .withFirstPrompt(new FirstPrompt()) 35 | //设置 每个Prompt问题的前缀 36 | .withPrefix(new MyPromptPrefix()) 37 | //是否显示(玩家|控制台)的回复内容,默认为true 38 | .withLocalEcho(false) 39 | //是否阻塞非问答会话信息的展示(比如其他玩家的聊天、服务器公告等等),默认为true 40 | .withModality(false); 41 | } 42 | 43 | /** 44 | * 获取工厂单例 45 | */ 46 | public static ConversationFactory get() { 47 | return conversationFactory; 48 | } 49 | 50 | /** 51 | * 给玩家开启一次问答会话 52 | * @param playerOrConsole 玩家 53 | */ 54 | public static void start(Conversable playerOrConsole) { 55 | //Conversation对象创建后就直接使用,不需要实例化后再维护或调用它abandoned方法关闭本地问答会话。服务端会根据超时时间自动关闭会话。 56 | conversationFactory.buildConversation(playerOrConsole) 57 | .begin(); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/java/com/senko/customeventplugin/executor/GodBowCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.senko.customeventplugin.executor; 2 | 3 | import com.senko.customeventplugin.event.GodBowEvent; 4 | import com.senko.customeventplugin.pojo.GodBow; 5 | import com.senko.customeventplugin.utils.InventoryUtils; 6 | 7 | import org.bukkit.ChatColor; 8 | import org.bukkit.Material; 9 | import org.bukkit.command.Command; 10 | import org.bukkit.command.CommandExecutor; 11 | import org.bukkit.command.CommandSender; 12 | import org.bukkit.enchantments.Enchantment; 13 | import org.bukkit.entity.Player; 14 | import org.bukkit.inventory.Inventory; 15 | import org.bukkit.inventory.ItemStack; 16 | import org.bukkit.inventory.meta.ItemMeta; 17 | 18 | import java.util.LinkedList; 19 | 20 | /** 21 | * 给自己发一把类似于Hypixel的Modular Bow 22 | */ 23 | public class GodBowCommandExecutor implements CommandExecutor { 24 | 25 | @Override 26 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 27 | if (label.equalsIgnoreCase("godbow")) { 28 | // if (args != null && args.length == 1) { 29 | // sender.sendMessage("进入arg1模式"); 30 | // String msg = ChatColor.GREEN + "当前模式:" + ChatColor.YELLOW + "击退" + ChatColor.GRAY + "[1]"; 31 | // String str = ChatColor.translateAlternateColorCodes('\u00A7', msg); 32 | // System.out.println(str); 33 | // return true; 34 | // } 35 | if (sender instanceof Player && sender.hasPermission("senko.godbow")) { 36 | Player player = (Player) sender; 37 | if (!InventoryUtils.add(player.getInventory(), GodBow.createGodBow())) { 38 | //背包满了 39 | player.sendMessage(ChatColor.RED + "背包已满,请清理后再试"); 40 | return true; 41 | } else { 42 | //背包未满 43 | player.sendMessage(ChatColor.GREEN + "嘿!看看你的背包里多了个什么?"); 44 | return true; 45 | } 46 | } 47 | } 48 | return false; 49 | } 50 | 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /EventListener/.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 | -------------------------------------------------------------------------------- /ThreadPlugin/.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 | -------------------------------------------------------------------------------- /ConversationPlugin/src/main/java/com/senko/conversationplugin/conversation/promts/ThirdPrompt.java: -------------------------------------------------------------------------------- 1 | package com.senko.conversationplugin.conversation.promts; 2 | 3 | import org.bukkit.conversations.ConversationContext; 4 | import org.bukkit.conversations.PlayerNamePrompt; 5 | import org.bukkit.conversations.Prompt; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.plugin.Plugin; 8 | 9 | import java.util.Objects; 10 | 11 | /** 12 | * @author senko 13 | * @date 2022/7/7 10:55 14 | */ 15 | public class ThirdPrompt extends PlayerNamePrompt { 16 | 17 | /** 18 | * 由于需要判断玩家输入的名字能否找到对应的玩家,需要插件对象 19 | * @param plugin 插件对象 20 | */ 21 | public ThirdPrompt(Plugin plugin) { 22 | super(plugin); 23 | } 24 | 25 | /** 26 | * 同样的,我又对父类的isInputValid感到不满意了,于是手动@Overrider这个方法,自己对input进行判断 27 | * @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文 28 | * @param input 玩家输入的内容 29 | * @return 校验结果 30 | */ 31 | @Override 32 | protected boolean isInputValid(ConversationContext context, String input) { 33 | Player player = context.getPlugin().getServer().getPlayer(input); 34 | //如果玩家不存在,或玩家不在线,则校验失败 35 | if (Objects.isNull(player) || !player.isOnline()) { 36 | context.getForWhom().sendRawMessage("玩家不存在或不在线!"); 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | /** 43 | * 校验成功,开始处理最终结果,并通过返回null值来结束会话 44 | * @param context 存储 全局信息(当前的会话对象,插件,自定义信息)的上下文 45 | * @param input 46 | * @return 47 | */ 48 | @Override 49 | protected Prompt acceptValidatedInput(ConversationContext context, Player input) { 50 | /** 51 | * 可以在这里直接根据context存储的中间信息处理,然后return null结束会话 52 | * 53 | * 也可以跟我一样再返回一个MessagePrompt,提示个问题(友好信息)后返回null结束会话 54 | */ 55 | context.setSessionData("player", input); 56 | return new FinalFriendMessagePrompt(); 57 | // return Prompt.END_OF_CONVERSATION; 58 | } 59 | 60 | /** 61 | * 问题提示 62 | */ 63 | @Override 64 | public String getPromptText(ConversationContext context) { 65 | return "请输入玩家的名字来给它生成" + context.getSessionData("number") + "只" + context.getSessionData("animal") + ":"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Configuration/src/main/java/com/senko/configuration/executor/ChangeLocationExecutor.java: -------------------------------------------------------------------------------- 1 | package com.senko.configuration.executor; 2 | 3 | import com.senko.configuration.Configuration; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.Location; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.configuration.file.FileConfiguration; 10 | import org.bukkit.entity.Player; 11 | 12 | public class ChangeLocationExecutor implements CommandExecutor { 13 | @Override 14 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 15 | if (label.equalsIgnoreCase("change")) { 16 | if (args.length == 1) { 17 | if (sender instanceof Player) { 18 | Player player = Bukkit.getPlayerExact(args[0]); 19 | if (player != null) { 20 | Location curLocation = player.getLocation(); 21 | FileConfiguration config = Configuration.getPlugin().getConfig(); 22 | config.set(player.getName()+".location",curLocation); 23 | Configuration.getPlugin().saveConfig(); 24 | sender.sendMessage("标记成功!"); 25 | } 26 | return true; 27 | } 28 | } else if (args.length == 2 && args[1].equalsIgnoreCase("tp")) { 29 | if (sender instanceof Player) { 30 | FileConfiguration config = Configuration.getPlugin().getConfig(); 31 | Location location = config.getLocation(args[0] + ".location"); 32 | if (location == null) { 33 | sender.sendMessage("未找到标记地点!请检查Target名是否正确或重新标记!"); 34 | return false; 35 | } 36 | Player player = Bukkit.getPlayerExact(args[0]); 37 | if (player != null) { 38 | player.teleport(location); 39 | return true; 40 | }else { 41 | return false; 42 | } 43 | } 44 | } 45 | } 46 | return false; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /CustomEventPlugin/src/main/java/com/senko/customeventplugin/event/GodBowEvent.java: -------------------------------------------------------------------------------- 1 | package com.senko.customeventplugin.event; 2 | 3 | import com.senko.customeventplugin.enums.EnumBowMode; 4 | import com.senko.customeventplugin.holder.GodBowHolder; 5 | import com.senko.customeventplugin.pojo.GodBow; 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.Sound; 8 | import org.bukkit.entity.Entity; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.event.Cancellable; 11 | import org.bukkit.event.Event; 12 | import org.bukkit.event.HandlerList; 13 | import org.bukkit.inventory.ItemStack; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | public class GodBowEvent extends Event implements Cancellable { 18 | 19 | private static final HandlerList handlers = new HandlerList(); 20 | private static boolean isCancelled = false; 21 | 22 | @NotNull 23 | private GodBow godBow; 24 | @Nullable 25 | private Entity holder; 26 | 27 | public GodBowEvent(@NotNull ItemStack bow, @Nullable Entity holder) { 28 | this.godBow = new GodBow(bow); 29 | GodBowHolder.add(bow, this.godBow); 30 | this.holder = holder; 31 | } 32 | 33 | public void changeBowMode() { 34 | godBow.changeBowMode(); 35 | if (holder != null && holder instanceof Player) { 36 | Player player = (Player) this.holder; 37 | player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP,.5f, .5f); 38 | holder.sendMessage(ChatColor.GREEN + "当前神弓的模式为:" + godBow.getMode().getModeName()); 39 | } 40 | GodBowHolder.add(this.godBow.getBow(), this.godBow); 41 | } 42 | 43 | public EnumBowMode getMode() { 44 | return godBow.getMode(); 45 | } 46 | 47 | public GodBow getGodBow() { 48 | return godBow; 49 | } 50 | 51 | @Override 52 | public boolean isCancelled() { 53 | return isCancelled; 54 | } 55 | 56 | @Override 57 | public void setCancelled(boolean cancel) { 58 | this.isCancelled = cancel; 59 | } 60 | 61 | @Override 62 | public HandlerList getHandlers() { 63 | return handlers; 64 | } 65 | 66 | public static HandlerList getHandlerList() { 67 | return handlers; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /TextComponentPlugin/.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | # mpeltonen/sbt-idea plugin 11 | .idea_modules/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Package Files # 26 | *.jar 27 | *.war 28 | *.nar 29 | *.ear 30 | *.zip 31 | *.tar.gz 32 | *.rar 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | 37 | *~ 38 | 39 | # temporary files which can be created if a process still has a handle open of a deleted file 40 | .fuse_hidden* 41 | 42 | # KDE directory preferences 43 | .directory 44 | 45 | # Linux trash folder which might appear on any partition or disk 46 | .Trash-* 47 | 48 | # .nfs files are created when an open file is removed but is still being accessed 49 | .nfs* 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | # Windows thumbnail cache files 79 | Thumbs.db 80 | Thumbs.db:encryptable 81 | ehthumbs.db 82 | ehthumbs_vista.db 83 | 84 | # Dump file 85 | *.stackdump 86 | 87 | # Folder config file 88 | [Dd]esktop.ini 89 | 90 | # Recycle Bin used on file shares 91 | $RECYCLE.BIN/ 92 | 93 | # Windows Installer files 94 | *.cab 95 | *.msi 96 | *.msix 97 | *.msm 98 | *.msp 99 | 100 | # Windows shortcuts 101 | *.lnk 102 | 103 | .gradle 104 | build/ 105 | 106 | # Ignore Gradle GUI config 107 | gradle-app.setting 108 | 109 | # Cache of project 110 | .gradletasknamecache 111 | 112 | **/build/ 113 | 114 | # Common working directory 115 | run/ 116 | 117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 118 | !gradle-wrapper.jar 119 | -------------------------------------------------------------------------------- /Command/src/main/java/com/senko/command/commands/executors/MyCommand.java: -------------------------------------------------------------------------------- 1 | package com.senko.command.commands.executors; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.World; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.command.TabExecutor; 8 | import org.bukkit.entity.Player; 9 | 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | 13 | @Deprecated 14 | public class MyCommand implements TabExecutor { 15 | @Override 16 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 17 | if (sender instanceof Player) { 18 | if (command.getName().equals("shoot")) { 19 | Player player = (Player) sender; 20 | player.sendMessage("自杀神教万岁!(仅限UHC模式)"); 21 | player.setHealth(0); 22 | player.sendMessage(ChatColor.BLACK+"===== Label:" + label + " ====="); 23 | StringBuilder str = new StringBuilder(); 24 | for (String arg : args) { 25 | str.append(arg + ","); 26 | } 27 | player.sendMessage("Args: " + args.length + str); 28 | player.sendMessage("Command Info: " + command.getName()); 29 | 30 | World world = player.getWorld(); 31 | 32 | return true; 33 | } 34 | }else { 35 | sender.sendMessage("只有玩家才能使用该指令,"+sender.getName()+"你看看你自己是什么先~"); 36 | } 37 | return false; 38 | } 39 | 40 | @Override 41 | public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { 42 | LinkedList list = new LinkedList<>(); 43 | StringBuilder str = new StringBuilder(); 44 | if (sender instanceof Player) { 45 | list.add(ChatColor.BLACK+"===== onTabComplete:" + args.length + " =====\n"); 46 | for (String arg : args) { 47 | str.append(arg + ","); 48 | } 49 | list.add("\nArgs: " + args.length + str + "\n"); 50 | list.add("\nCommand Info: " + command.getName() + "\n"); 51 | }else { 52 | sender.sendMessage("只有玩家才能使用该指令,"+sender.getName()+"你看看你自己是什么先~"); 53 | } 54 | return list; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /CustomRecipe/src/main/java/com/senko/customrecipe/CustomRecipe.java: -------------------------------------------------------------------------------- 1 | package com.senko.customrecipe; 2 | 3 | import com.senko.customeventplugin.pojo.GodBow; 4 | 5 | 6 | import com.senko.customrecipe.executor.VillagerCommandExecutor; 7 | import com.sun.org.apache.bcel.internal.generic.NEW; 8 | import org.bukkit.*; 9 | import org.bukkit.entity.Entity; 10 | import org.bukkit.entity.EntityType; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.entity.Villager; 13 | import org.bukkit.inventory.*; 14 | import org.bukkit.inventory.meta.ItemMeta; 15 | import org.bukkit.plugin.java.JavaPlugin; 16 | 17 | import java.awt.font.TextHitInfo; 18 | import java.util.*; 19 | 20 | public final class CustomRecipe extends JavaPlugin { 21 | 22 | /** 23 | * 命名空间 {@link NamespacedKey}不可重复使用在不同配方里, 24 | * 不可重名,否则会抛出 25 | * {@link IllegalArgumentException}异常 26 | */ 27 | @Override 28 | public void onEnable() { 29 | /** 30 | * 熔炉自定义 31 | */ 32 | //命名空间 33 | NamespacedKey key1 = new NamespacedKey(this, "furnace_recipe"); 34 | //煅烧结果 35 | ItemStack godBow = GodBow.createGodBow(); 36 | //熔炉配方 37 | FurnaceRecipe furnaceRecipe = new FurnaceRecipe(key1,godBow,Material.DIRT,0f,3 * 20); 38 | //加入到服务器中 39 | Bukkit.addRecipe(furnaceRecipe); 40 | 41 | /** 42 | * 自定义工作台物品合成 43 | * Shaped 定形 44 | * Shapeless 不定形 45 | * rows 46 | * [" P", " S", " "] 47 | * 48 | * " P" 49 | * " S" 50 | * " " 51 | * 52 | * "P " 53 | * "S " 54 | * " " 55 | */ 56 | //命名空间 57 | NamespacedKey craftKey = new NamespacedKey(this, "craft_table"); 58 | //结果 59 | ShapedRecipe shapedRecipe = new ShapedRecipe(craftKey, godBow); 60 | //原材料的摆放方式 61 | shapedRecipe.shape( 62 | " P", 63 | " S", 64 | " " 65 | ); 66 | //指定摆放方式中的字符所代表的原材料 67 | shapedRecipe.setIngredient('P', Material.BLAZE_POWDER); 68 | shapedRecipe.setIngredient('S', Material.SLIME_BALL); 69 | //加入到服务器中 70 | Bukkit.addRecipe(shapedRecipe); 71 | 72 | /** 73 | * 村民的自定义交易 74 | */ 75 | getCommand("villager").setExecutor(new VillagerCommandExecutor()); 76 | 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /SimpleCustomEvent/src/main/java/com/senko/simplecustomevent/event/listener/TestEventListener.java: -------------------------------------------------------------------------------- 1 | package com.senko.simplecustomevent.event.listener; 2 | 3 | 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.EventPriority; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.player.PlayerToggleSneakEvent; 9 | import org.bukkit.plugin.RegisteredListener; 10 | 11 | /** 12 | * 有关EventPriority优先级、ignoreCancelled以及HandlerList的小研究 13 | */ 14 | public class TestEventListener implements Listener { 15 | 16 | /** 17 | * EventPriority的优先级越高,对应的Handler就越往后才处理, 18 | * 因此EventPriority.LOWEST标注的Handler是最先执行的。 19 | */ 20 | @EventHandler(priority = EventPriority.NORMAL) 21 | public void onPlayerSneakEvent(PlayerToggleSneakEvent event) { 22 | if (event.isSneaking()) { 23 | event.getPlayer().sendMessage("这是优先级NORMAL的事件监听,将会在LOWEST优先级的监听器执行之后出现!"); 24 | } 25 | } 26 | 27 | @EventHandler(priority = EventPriority.LOWEST) 28 | public void onPlayerSneakEvent2(PlayerToggleSneakEvent event) { 29 | if (event.isSneaking()) { 30 | event.getPlayer().sendMessage("这是优先级LOWEST的事件监听,将会最先出现!"); 31 | } 32 | } 33 | 34 | @EventHandler(priority = EventPriority.HIGH) 35 | public void onPlayerSneakEvent3(PlayerToggleSneakEvent event) { 36 | if (event.isSneaking()) { 37 | RegisteredListener[] registeredListeners = event.getHandlers().getRegisteredListeners(); 38 | Player player = event.getPlayer(); 39 | 40 | player.sendMessage("负责监听PlayerToggleSneakEvent的监听器:"); 41 | int i = 1; 42 | for (RegisteredListener registeredListener : registeredListeners) { 43 | player.sendMessage("监听器" + i++ + "号所在插件:" + registeredListener.getPlugin().getName()); 44 | event.getPlayer().sendMessage("所在类:" + registeredListener.getListener().getClass().getSimpleName()); 45 | } 46 | 47 | event.setCancelled(true); 48 | } 49 | } 50 | 51 | /** 52 | * 如果正在监听的event对象被前面的handler改为了cancel状态, 53 | * 则这里的handler方法不会被执行。 54 | */ 55 | @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) 56 | public void onPlayerSneakEvent4(PlayerToggleSneakEvent event) { 57 | if (event.isSneaking()) { 58 | event.getPlayer().sendMessage("这段代码执行了吗?"); 59 | } 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /PotionPlugin/src/main/java/com/senko/potion/executor/GetPotion.java: -------------------------------------------------------------------------------- 1 | package com.senko.potion.executor; 2 | 3 | import org.bukkit.Color; 4 | import org.bukkit.Material; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.bukkit.inventory.PlayerInventory; 11 | import org.bukkit.inventory.meta.PotionMeta; 12 | import org.bukkit.potion.PotionEffect; 13 | import org.bukkit.potion.PotionEffectType; 14 | 15 | import java.util.Arrays; 16 | import java.util.stream.Stream; 17 | 18 | /** 19 | * @author senko 20 | * @date 2022/8/7 20:28 21 | */ 22 | public class GetPotion implements CommandExecutor { 23 | 24 | @Override 25 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 26 | 27 | if (sender instanceof Player) { 28 | Player player = (Player) sender; 29 | 30 | if (-1 != player.getInventory().firstEmpty()) { 31 | PlayerInventory inventory = player.getInventory(); 32 | 33 | ItemStack potionStack = new ItemStack(Material.POTION); // 创建物品槽,用于存放药水 34 | 35 | PotionMeta potionMeta = (PotionMeta) potionStack.getItemMeta(); // 得到药水的meta 36 | addAllPotionEffects(potionMeta); // 添加所有药水效果 37 | potionMeta.setColor(Color.fromRGB(178,176,170)); // 修改药水的颜色 38 | // potionMeta.setColor(Color.GREEN); // 修改药水的颜色 39 | 40 | potionStack.setItemMeta(potionMeta); 41 | inventory.addItem(potionStack); 42 | } else { 43 | sender.sendMessage("背包已满!"); 44 | } 45 | 46 | return true; 47 | } 48 | sender.sendMessage("只有玩家才能使用当前指令!"); 49 | return true; 50 | 51 | } 52 | 53 | private void addAllPotionEffects(PotionMeta potionMeta) { 54 | Stream.of(PotionEffectType.values()) 55 | // 遍历Type 56 | .forEach(potionEffectType -> { 57 | // 给meta添加一个药水效果 58 | potionMeta.addCustomEffect( 59 | // 10秒、一级、粒子效果更明显,ui界面带有图标的药水效果 60 | new PotionEffect(potionEffectType, 20 * 10, 0, false, true, true), 61 | // 如果玩家存在该药水效果,则强制替换 62 | true 63 | ); 64 | }); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /TextComponentPlugin/src/main/java/com/senko/textcomponentplugin/executor/PrintComponentBuilder.java: -------------------------------------------------------------------------------- 1 | package com.senko.textcomponentplugin.executor; 2 | 3 | import net.md_5.bungee.api.ChatColor; 4 | import net.md_5.bungee.api.chat.*; 5 | import net.md_5.bungee.api.chat.hover.content.Text; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | 11 | import java.util.Objects; 12 | 13 | /** 14 | * @author senko 15 | * @date 2022/6/10 10:45 16 | */ 17 | public class PrintComponentBuilder implements CommandExecutor { 18 | ChatColor color1 = ChatColor.of("#FCF3CF"); //十六进制的颜色码 19 | ChatColor color2 = ChatColor.of("#F9E79F"); //十六进制的颜色码 20 | ChatColor color3 = ChatColor.of("#F7DC6F"); //... 21 | ChatColor color4 = ChatColor.of("#F4D03F"); 22 | ChatColor color5 = ChatColor.of("#F1C40F"); 23 | ChatColor color6 = ChatColor.of("#D4AC0D"); 24 | 25 | @Override 26 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 27 | 28 | if (sender instanceof Player) { 29 | Player player = (Player) sender; 30 | 31 | executeFun(player); 32 | 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | private void executeFun(Player player) { 40 | BaseComponent[] components1 = new ComponentBuilder() 41 | .append("大").color(color1).bold(true) 42 | .append("家").color(color2).italic(true) 43 | .append("好").color(color3).obfuscated(true) 44 | .append("啊").color(color4).underlined(true) 45 | .create(); 46 | 47 | //为什么需要单独再构建出一个components2? 是为了防止继承上一个组件的属性,下面的components3同理 48 | BaseComponent[] components2 = new ComponentBuilder() 49 | .append("欢").color(color5) 50 | .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "虽然是发送指令,可实际上只是说了句话罢了")) 51 | .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("这个字可以点哦"))) 52 | .create(); 53 | 54 | BaseComponent[] components3 = new ComponentBuilder() 55 | .append("迎").color(color6) //不单独构造,则会继承上一个组件的event事件等属性 56 | .create(); 57 | 58 | TextComponent[] result = new TextComponent[]{new TextComponent(components1), new TextComponent(components2), new TextComponent(components3)}; 59 | //发送 组件 60 | player.spigot().sendMessage(result); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Command/src/main/java/com/senko/command/Command.java: -------------------------------------------------------------------------------- 1 | package com.senko.command; 2 | 3 | import com.senko.command.commands.executors.GodCommandExecutor; 4 | import com.senko.command.commands.handler.CommandHandler; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.plugin.Plugin; 9 | import org.bukkit.plugin.PluginManager; 10 | import org.bukkit.plugin.java.JavaPlugin; 11 | 12 | import java.util.logging.Logger; 13 | 14 | public final class Command extends JavaPlugin { 15 | private String name = "abc"; 16 | @Override 17 | public void onEnable() { 18 | 19 | /** 20 | * 1. 在插件的描述plugin.yml文件中声明Command指令 21 | * 2. 编写指令的CommandExecutor执行器 22 | * 3. 在当前插件获取指令,并绑定CommandExecutor执行器。 23 | */ 24 | 25 | this.getCommand("suicide").setExecutor(this); 26 | this.getCommand("god").setExecutor(new GodCommandExecutor()); 27 | 28 | 29 | /** 30 | * tc子指令执行器,在CommandExecutor里维护多个自定义的指令,通过args[index]进行匹配调用。 31 | */ 32 | this.getCommand("tc").setExecutor(new CommandHandler()); 33 | 34 | } 35 | 36 | /** 37 | * /suicide 指令执行器 38 | * @param sender 指令的发送者:可以是玩家、远程控制台、本地控制台、命令方块、其他实现了Permissible接口的实现类 39 | * @param command 指令:就是this.getCommand("suicide")的返回值,存储着在plugin.yml中声明的各种指令信息 40 | * @param label 指令的名称:如/suicide的指令label就是 "suicide" 41 | * @param args 指令的参数:加入输入的完整指令是/suicide 1 2 3,那么该args数组就是 [1, 2, 3] 42 | * @return 43 | */ 44 | @Override 45 | public boolean onCommand(CommandSender sender, org.bukkit.command.Command command, String label, String[] args) { 46 | /** 47 | * 自杀指令:只有玩家能使用 48 | */ 49 | if (label.equalsIgnoreCase("suicide")) { 50 | if (sender instanceof Player) { 51 | //输入指令的对象是Player玩家 52 | Player player = (Player) sender; 53 | //向全服玩家广播信息 54 | player.getServer().broadcastMessage(ChatColor.YELLOW + "再见了,世界!" + 55 | "\n" + ChatColor.BLUE + player.getDisplayName() + ChatColor.YELLOW + "趋势了:/"); 56 | player.setHealth(0); 57 | 58 | //return true是为了防止服务器给sender返回 指令的usage信息 59 | return true; 60 | }else { 61 | sender.sendMessage(ChatColor.YELLOW + "只有玩家能使用"); 62 | return true; 63 | } 64 | } 65 | 66 | //返回false:自动调用plugin.yml配置的commands.suicide.usage值并返回给CommandSender指令发送者 67 | return false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /featureTest/src/main/java/com/senko/featuretest/FeatureTest.java: -------------------------------------------------------------------------------- 1 | package com.senko.featuretest; 2 | 3 | import com.senko.featuretest.event.RecipeCraftEvent; 4 | import com.senko.featuretest.listener.OnEatEventListener; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.Location; 7 | import org.bukkit.Material; 8 | import org.bukkit.NamespacedKey; 9 | import org.bukkit.command.Command; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.inventory.ItemStack; 13 | import org.bukkit.inventory.ShapedRecipe; 14 | import org.bukkit.plugin.java.JavaPlugin; 15 | 16 | public final class FeatureTest extends JavaPlugin { 17 | 18 | @Override 19 | public void onEnable() { 20 | 21 | // Plugin startup logic 22 | getCommand("testL").setExecutor(this); 23 | Bukkit.getPluginManager().registerEvents(new OnEatEventListener(), this); 24 | Bukkit.getPluginManager().registerEvents(new RecipeCraftEvent(), this); 25 | registerRecipe(); 26 | } 27 | 28 | private void registerRecipe() { 29 | ShapedRecipe shapedRecipe = new ShapedRecipe(new NamespacedKey(this, "superStick"), new ItemStack(Material.DIAMOND_SWORD, 1)) 30 | .shape(" ", 31 | " A ", 32 | " ") 33 | .setIngredient('A', Material.STICK); 34 | Bukkit.addRecipe(shapedRecipe); 35 | } 36 | 37 | @Override 38 | public void onDisable() { 39 | // Plugin shutdown logic 40 | } 41 | 42 | @Override 43 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 44 | /** 45 | * 对非克隆得到的Location副本进行修改,会影响到原Location对象的值 46 | */ 47 | if (label.equalsIgnoreCase("testl") && args.length == 1 && args[0].equalsIgnoreCase("copy") && sender instanceof Player) { 48 | Player player = (Player) sender; 49 | Location location = player.getLocation(); 50 | 51 | //非Clone 52 | Location copy1 = location; 53 | copy1.add(0, 2, 0); 54 | player.teleport(location); 55 | return true; 56 | } else if (label.equalsIgnoreCase("testl") && args.length == 1 && args[0].equalsIgnoreCase("clone") && sender instanceof Player) { 57 | Player player = (Player) sender; 58 | Location location = player.getLocation(); 59 | 60 | //clone 61 | Location clone1 = location.clone(); 62 | player.teleport(location); 63 | return true; 64 | } 65 | return false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /InventoryPlugin/src/main/java/com/senko/inventoryplugin/executor/RemoteInventoryCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.senko.inventoryplugin.executor; 2 | 3 | import com.senko.inventoryplugin.holder.GlobalInventory; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.Material; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandExecutor; 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.enchantments.Enchantment; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.inventory.Inventory; 13 | import org.bukkit.inventory.InventoryView; 14 | import org.bukkit.inventory.ItemStack; 15 | import org.bukkit.inventory.meta.ItemMeta; 16 | 17 | import java.util.LinkedList; 18 | 19 | public class RemoteInventoryCommandExecutor implements CommandExecutor { 20 | 21 | 22 | @Override 23 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 24 | if (label.equalsIgnoreCase("open")) { 25 | if (sender instanceof Player) { 26 | Player curPlayer = (Player) sender; 27 | Inventory inventory = null; 28 | if (!GlobalInventory.contains(curPlayer.getName())) { 29 | inventory = Bukkit.createInventory(curPlayer, 9, curPlayer.getName() + "的箱子"); 30 | 31 | //一个槽 存羽毛 32 | ItemStack itemStack = new ItemStack(Material.FEATHER, 1); 33 | 34 | //获取羽毛的信息对象 35 | ItemMeta itemMeta = itemStack.getItemMeta(); 36 | //修改物品名称 37 | itemMeta.setDisplayName(ChatColor.RED + "小鸡毛"); 38 | 39 | //List表,一个元素代表一行 40 | LinkedList lores = new LinkedList<>(); 41 | lores.add(ChatColor.YELLOW + "第一:绝不轻言放弃"); 42 | lores.add(ChatColor.GREEN + "第二:绝不高傲"); 43 | //修改物品的表示 44 | itemMeta.setLore(lores); 45 | 46 | 47 | itemStack.setItemMeta(itemMeta); 48 | itemStack.addUnsafeEnchantment(Enchantment.LUCK, 666); 49 | inventory.addItem(itemStack); 50 | 51 | GlobalInventory.addToInventories(curPlayer.getName(), inventory); 52 | } else { 53 | inventory = GlobalInventory.getInventory(curPlayer.getName()); 54 | } 55 | 56 | curPlayer.openInventory(inventory); 57 | } else { 58 | sender.sendMessage("只有玩家才能使用该指令"); 59 | return true; 60 | } 61 | } 62 | return false; 63 | } 64 | } 65 | 66 | -------------------------------------------------------------------------------- /MapTutorialPlugin/src/main/java/com/senko/maptutorialplugin/render/FirstRender.java: -------------------------------------------------------------------------------- 1 | package com.senko.maptutorialplugin.render; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Location; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.map.*; 7 | 8 | import javax.imageio.ImageIO; 9 | import java.awt.*; 10 | import java.io.IOException; 11 | import java.net.URL; 12 | 13 | public class FirstRender extends MapRenderer { 14 | 15 | public FirstRender() { 16 | /** 17 | * 不分开渲染 18 | */ 19 | super(false); 20 | 21 | try { 22 | image = ImageIO.read(new URL("https://www.baidu.com/img/bd_logo1.png")); 23 | } catch (IOException e) { 24 | throw new RuntimeException(e); 25 | } 26 | } 27 | 28 | private Image image; 29 | 30 | private MapCursorCollection cursors = new MapCursorCollection(); 31 | 32 | { 33 | MapCursor cursor1 = new MapCursor((byte) 0, (byte) 0, (byte) 4, MapCursor.Type.MANSION, true); 34 | MapCursor cursor2 = new MapCursor((byte) 0, (byte) 0, (byte) 0, MapCursor.Type.BANNER_PURPLE, true); 35 | cursors.addCursor(cursor1); 36 | cursors.addCursor(cursor2); 37 | } 38 | 39 | public void setImage(Image image) { 40 | this.image = image; 41 | } 42 | 43 | /** 44 | * 高频率调用,所以请务必不要在这里进行很耗时的操作 45 | */ 46 | @Override 47 | public void render(MapView map, MapCanvas canvas, Player player) { 48 | // if (null != player) { 49 | // Bukkit.broadcastMessage(player.getName() + "玩家,你好!现在是:" + System.currentTimeMillis()); 50 | // } else { 51 | // Bukkit.broadcastMessage("游客,你好!现在是:" + System.currentTimeMillis()); 52 | // } 53 | 54 | /** 55 | * 需改为128 X 128 像素大小的图片 56 | */ 57 | canvas.drawImage(0, 0, image); 58 | /** 59 | * 在View中渲染英文字符(不要带有MinecraftFont.Font中不存在的字符) 60 | */ 61 | canvas.drawText((int) player.getLocation().getX(), (int) player.getLocation().getZ(), MinecraftFont.Font, "Hello World!"); 62 | 63 | MapCursor cursor1 = cursors.getCursor(0); // 宅邸 64 | MapCursor cursor2 = cursors.getCursor(1); // 旗帜 65 | 66 | Location senkosan = Bukkit.getPlayer("Senkosan").getLocation(); 67 | byte x = (byte) senkosan.getX(); 68 | byte y = (byte) senkosan.getZ(); 69 | 70 | Location iLiveOnFaith = Bukkit.getPlayer("iLiveOnFaith").getLocation(); 71 | byte x2 = (byte) iLiveOnFaith.getX(); 72 | byte y2 = (byte) iLiveOnFaith.getZ(); 73 | 74 | cursor1.setX(x); 75 | cursor1.setY(y); 76 | 77 | cursor2.setX(x2); 78 | cursor2.setY(y2); 79 | 80 | canvas.setCursors(cursors); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /BlockPlugin/src/main/java/com/senko/blockplugin/examples/block/BlockExample.java: -------------------------------------------------------------------------------- 1 | package com.senko.blockplugin.examples.block; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Location; 5 | import org.bukkit.Material; 6 | import org.bukkit.block.Biome; 7 | import org.bukkit.block.Block; 8 | import org.bukkit.block.BlockFace; 9 | import org.bukkit.entity.Player; 10 | 11 | /** 12 | * 有关Block方块的示例 13 | * @author senko 14 | * @date 2022/7/15 10:38 15 | */ 16 | public class BlockExample { 17 | 18 | /** 19 | * {@link Block}的存在就跟{@link org.bukkit.inventory.ItemStack}类似,它不保存方块的内部信息, 20 | * 真正的方块内部信息分如箱子内容、告示牌内容则由{@link Block#getState()}所得到的对象存储。 21 | * 正因为有这么多复杂多变的属性,我们不可能通过简单的方法去new出一个方块, 22 | * 因此正常的做法是通过 事件系统或{@link org.bukkit.World#getBlockAt(Location)} 等方式 23 | * 来“获取” 方块,而非 ”new“ 出方块。 24 | * 25 | * Block方块主要代表了在当前世界。当前坐标下这个方块的状态信息, 26 | * 比如方块是什么、光照等级是多少、生态群系是什么、它的坐标是什么,它所在的世界是什么等等 27 | * 28 | * {@link org.bukkit.block.BlockState}里的内容看着好像和{@link Block}没什么不同, 29 | * 但它的作用并不是表面的这些,而是转型成相应的子类,得到特殊方块的内部信息。比如它的子类箱子{@link org.bukkit.block.Chest} 30 | * 命令方块{@link org.bukkit.block.CommandBlock}。 31 | */ 32 | public void doBlockAPI(Block block, Player player) { 33 | 34 | Material type = block.getType(); 35 | player.sendMessage("方块类型:" + type.toString()); 36 | 37 | Block relativeEast = block.getRelative(BlockFace.EAST); 38 | player.sendMessage("与方块相邻的东边的方块:" + relativeEast.getType().toString()); 39 | 40 | Location location = block.getLocation(); 41 | player.sendMessage("方块的坐标:" + location.toString()); 42 | 43 | Biome biome = block.getBiome(); 44 | player.sendMessage("方块所在的生态群系:" + biome.toString()); 45 | 46 | int blockPower = block.getBlockPower(); 47 | player.sendMessage("方块的红石等级:" + blockPower); 48 | 49 | int blockLightLevel = block.getLightLevel(); 50 | player.sendMessage("方块的光照等级:" + blockLightLevel); 51 | 52 | //block.getChunk(); // 获取方块所在的Chunk,Chunk API有关,本期视频不细讲 53 | 54 | byte lightLevel = block.getLightLevel(); 55 | player.sendMessage("方块的光照等级:" + lightLevel); 56 | //...还有更多API,自己发掘吧 57 | 58 | // 修改方块的生态群戏 59 | block.setBiome(Biome.BADLANDS); 60 | 61 | // 生成掉落中的方块 62 | Location blockLocation = block.getLocation(); 63 | Location clonedLocation = blockLocation.clone(); // 获取Location位置的副本,防止修改location会同步影响到原方块 64 | clonedLocation.add(0, 10, 0); // y轴增加10格 65 | block.getWorld().spawnFallingBlock(clonedLocation, Bukkit.createBlockData(Material.TNT)); 66 | 67 | /** 68 | * 真正负责存储方块内部数据的两个类 69 | */ 70 | // block.getState(); 71 | // block.getBlockData(); 72 | 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /BlockPlugin/src/main/java/com/senko/blockplugin/examples/blockstate/BlockStateExample.java: -------------------------------------------------------------------------------- 1 | package com.senko.blockplugin.examples.blockstate; 2 | 3 | import org.bukkit.Material; 4 | import org.bukkit.block.Block; 5 | import org.bukkit.block.BlockState; 6 | import org.bukkit.block.CommandBlock; 7 | import org.bukkit.entity.Player; 8 | 9 | /** 10 | * 方块状态的示例 11 | * @author senko 12 | * @date 2022/7/15 10:27 13 | */ 14 | public class BlockStateExample { 15 | 16 | public void doGetBlockState(Block block, Player player) { 17 | 18 | // 如果被点击的方块是命令方块 19 | if (block.getType() == Material.COMMAND_BLOCK) { 20 | 21 | /** 22 | * BlockState是个基类,每个Block都具有, 23 | * 但是对于一些特殊的,可以存放信息的方块,比如命令方块,告示牌,箱子等, 24 | * 则有相应的BlockState的子类,比如{@link CommandBlock},{@link org.bukkit.block.Sign},{@link org.bukkit.block.Chest}等, 25 | * (注意,对于部分方块,他们存在同名的接口。比如{@link org.bukkit.block.Chest} 和 {@link org.bukkit.block.data.type.Chest}, 26 | * 一个是BlockState的子类,一个是BlockData的子类 27 | * 28 | * 对于这些特殊方块,我们可以对BlockState其转型,然后获取其中的信息。 29 | * 30 | * API注解没提示到,这里返回的State对象是一个副本,你针对副本的修改不会立即生效。 31 | * 而且这意味着其它插件可能会更改方块的状态, 而你的插件不会知道其变更; 32 | * 或者其它插件可能会将方块更改为另一种类型, 从而导致你的BlockState失效. 33 | * 所以需要在最后通过 {@link BlockState#update()} 来更新方块的状态. 34 | */ 35 | CommandBlock state = (CommandBlock) block.getState(); // 转型为子类 36 | 37 | /** 38 | * 比如在这,我们就可以获取到命令方块的命令。 39 | * 这些方块内部的详细数据,都是由{@link org.bukkit.block.BlockState}来存储的。 40 | * 并不是{@link org.bukkit.block.data.BlockData} 41 | */ 42 | String command = state.getCommand(); // 获取命令方块里的命令 43 | player.sendMessage("你点击的方块的命令是:" + command); 44 | 45 | /** 46 | * 同时我们还可以修改BlockState,但是任何有关BlockState的修改操作, 47 | * 都需要调用{@link BlockState#update()} 来完成对方块的更新。 48 | * 这也是为什么在第#14期视频里,我们用到了该方法。 49 | * 50 | * 这种做法也会导致触发方块更新,因此update方法也有个applyPhysics参数 51 | * {@link BlockState#update(boolean, boolean)} 52 | */ 53 | state.setCommand("/say hello"); // 修改BlockState 54 | player.sendMessage("现在它的命令变成/say hello了,你看看是不是。"); 55 | state.update(); // 更新BlockState 56 | // BlockState的更新也可以触发方块更新,因此update也有个带有applyPhysic参数的同名方法 57 | // state.update(true, false); // 强制更新方块状态,并不会引发周围方块的方块更新 58 | 59 | } else { 60 | player.sendMessage("你点击的方块不是命令方块"); 61 | } 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /PotionPlugin/src/main/java/com/senko/potion/executor/ApplyEffect.java: -------------------------------------------------------------------------------- 1 | package com.senko.potion.executor; 2 | 3 | import org.bukkit.command.Command; 4 | import org.bukkit.command.CommandExecutor; 5 | import org.bukkit.command.CommandSender; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.potion.PotionEffect; 8 | import org.bukkit.potion.PotionEffectType; 9 | import org.bukkit.potion.PotionEffectTypeWrapper; 10 | import org.bukkit.potion.PotionType; 11 | 12 | /** 13 | * @author senko 14 | * @date 2022/8/7 20:19 15 | */ 16 | public class ApplyEffect implements CommandExecutor { 17 | 18 | @Override 19 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 20 | 21 | if (sender instanceof Player) { 22 | if (args.length >= 1) { 23 | Player player = (Player) sender; 24 | 25 | PotionEffect potionEffect = null; 26 | switch (args[0].toLowerCase()) { 27 | case "enum": 28 | // /potion enum 29 | // 枚举模板创建 30 | potionEffect = getPotionEffectFromEnum(); 31 | break; 32 | case "new": 33 | // /potion new 34 | // 直接new出 35 | potionEffect = getPotionEffectFromNew(); 36 | break; 37 | default: 38 | sender.sendMessage("未知的命令"); 39 | return true; 40 | } 41 | 42 | // 给实体赋予药水效果 43 | // potionEffect.apply(player); // 两个是同一个方法 44 | player.addPotionEffect(potionEffect); 45 | return true; 46 | } 47 | 48 | sender.sendMessage("请输入一个效果:[enum, new]"); 49 | return true; 50 | } 51 | sender.sendMessage("只有玩家才能使用当前指令!"); 52 | return true; 53 | 54 | } 55 | 56 | private PotionEffect getPotionEffectFromEnum() { 57 | 58 | // 挑选一个枚举值,得到PotionEffectType 59 | // 利用PotionEffectType默认的参数new出一个PotionEffect 60 | return PotionEffectType.SPEED.createEffect(20 * 10, 5); 61 | 62 | } 63 | 64 | private PotionEffect getPotionEffectFromNew() { 65 | 66 | /** 67 | * 直接new出一个PotionEffect 68 | * 第一个参数:PotionEffectType 药水效果 69 | * 第二个参数:duration 20 * 10 ticks 持续时间 70 | * 第三个参数:amplifier [0~XXX] 药水等级,0:一级,1:二级... 71 | * 第四个参数:ambient [true|false] true时,生物周围的药水粒子更少,颜色更透明 72 | * 第五个参数:particles [true|false] true时,生物周围会有粒子效果 73 | * 第六个参数:showIcon [true|false] false时,玩家的主界面不会出现药水图标 74 | */ 75 | return new PotionEffect(PotionEffectType.SPEED, 20 * 10, 0, false, true, true); 76 | 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Command/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | Command 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | Command 13 | 14 | 玩家指令发送 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ParticleShootPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | github.shinyoki 8 | ParticleShootPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | ParticleShootPlugin 13 | 14 | 15 | 1.8 16 | UTF-8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.8.1 25 | 26 | ${java.version} 27 | ${java.version} 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 3.2.4 34 | 35 | 36 | package 37 | 38 | shade 39 | 40 | 41 | false 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | src/main/resources 50 | true 51 | 52 | 53 | 54 | 55 | 56 | 57 | spigotmc-repo 58 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 59 | 60 | 61 | sonatype 62 | https://oss.sonatype.org/content/groups/public/ 63 | 64 | 65 | 66 | 67 | 68 | org.spigotmc 69 | spigot-api 70 | 1.20.1-R0.1-SNAPSHOT 71 | provided 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /PotionPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | potion 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | PotionPlugin 13 | 14 | 了解自定义药水效果 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /BlockPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | blockPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | BlockPlugin 13 | 14 | 了解方块在插件中的体现 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /EventListener/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | EventListener 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | EventListener 13 | 14 | 事件监听器 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /featureTest/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | featureTest 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | FeatureTest 13 | 14 | 测试一些特性或理论问题 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Configuration/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | Configuration 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | Configuration 13 | 14 | 配置读取、设置 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /AttributePlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | attributePlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | AttributePlugin 13 | 14 | 为物品添加属性加成 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ScoreBoardPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | scoreBoardPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | ScoreBoardPlugin 13 | 14 | 学习如何自定义积分板 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /SimpleCustomEvent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | simpleCustomEvent 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | SimpleCustomEvent 13 | 14 | 自定义事件 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ThreadPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | ThreadPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | ThreadPlugin 13 | 14 | 测试Spigot插件的线程调用 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.spigotmc 71 | spigot-api 72 | 1.18-R0.1-SNAPSHOT 73 | provided 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /InventoryPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | InventoryPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | InventoryPlugin 13 | 14 | 测试插件对物品栏的操作 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /MapTutorialPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | MapTutorialPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | MapTutorialPlugin 13 | 14 | 快速上手了解Map API 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /TabExecutorPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | TabExecutorPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | TabExecutorPlugin 13 | 14 | 指令填充提示案例插件 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ConversationPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | ConversationPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | ConversationPlugin 13 | 14 | 上手了解ConversationAPI 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | org.spigotmc 70 | spigot-api 71 | 1.18-R0.1-SNAPSHOT 72 | provided 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /TextComponentPlugin/src/main/java/com/senko/textcomponentplugin/TextComponentPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.textcomponentplugin; 2 | 3 | import com.senko.textcomponentplugin.executor.*; 4 | import net.md_5.bungee.api.chat.BaseComponent; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | import java.util.logging.Logger; 9 | 10 | public final class TextComponentPlugin extends JavaPlugin { 11 | //插件单例 12 | private static TextComponentPlugin instance; 13 | 14 | public static TextComponentPlugin getInstance() { 15 | return instance; 16 | } 17 | 18 | /** 19 | * 文本样式:
20 | * (一):基于ChatColor实现的纯文本。
21 | * ChatColor有两类,一类是Bukkit的 {@link ChatColor},常用于拼接字符串常量和替换字符串中的字符为颜色代码§。
22 | * 另一类是{@link net.md_5.bungee.api.ChatColor},用作{@link BaseComponent}组件的属性,用于表示组件信息的样式。
23 | * (二):另一种是基于Bungeecord的{@link BaseComponent} ,他需要{@link net.md_5.bungee.api.ChatColor}等对象作为属性,用于确定当前组件的文本样式,需要注意的是{@link BaseComponent}并不是简单的String字符串, 24 | * 而是货真价实的类,最后要通过某种方法将他当做文本反馈给玩家。如player#sendMessage、player.spigot()#sendMessaege()

25 | * 像 [可点击的文本ChatText,玩家列表Tablist...] 这些特殊的文本,都能在 {@link BaseComponent} 的子类里找到相应的类。
26 | * 这里推荐一个可以查看颜色代码的网站:点击此处
27 | * 颜色代码wiki页面: 点击此处
28 | *

29 | * 30 | * 小技巧:
31 | * Bukkit的ChatColor提供了 {@link ChatColor#translateAlternateColorCodes(char, String)}函数,可以将String字符串中的特殊字符char替换为颜色代码§,
32 | * 比如:
33 | * 34 | * ChatColor.translateAlternateColorCodes('&', "&l&6你好啊&e我的朋友"); 35 | * 36 | *
37 | * 结果是:"§l§6你好啊§e我的朋友" 只有这种带有§字符的文本才能正确显示文本样式

38 | * 39 | * Bungeecord-ChatColor有个类函数{@link net.md_5.bungee.api.ChatColor#of(String)},可以将HEX CODE利用起来,从而实现更加多样化的文本色彩样式。 40 | */ 41 | @Override 42 | public void onEnable() { 43 | //注册指令 /help TextComponentPlugin 44 | getCommand("print").setExecutor(new PrintCommand()); 45 | getCommand("print-message").setExecutor(new PrintMessage()); //展示ChatColor的两种用法 46 | getCommand("print-title").setExecutor(new PrintTitle()); //向玩家的 屏幕中心 展示标题 47 | getCommand("print-text-component").setExecutor(new PrintTextComponent()); //向玩家的 聊天框、物品栏上方 展示信息 48 | getCommand("print-component-builder").setExecutor(new PrintComponentBuilder()); //展示 ComponentBuilder 的用法 49 | getCommand("print-boss-bar").setExecutor(new PrintBossBar()); 50 | getCommand("print-key-bind").setExecutor(new PrintKeyBind()); 51 | instance = this; 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /TextComponentPlugin/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /HelloWorld/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | HelloWorld 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | HelloWorld 13 | 14 | 这是我的第一个Minecraft Spigot Plugin 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | 33 | org.apache.maven.plugins 34 | maven-shade-plugin 35 | 3.2.4 36 | 37 | 38 | package 39 | 40 | shade 41 | 42 | 43 | false 44 | 45 | E:\Minecraft\spigot\plugins 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | src/main/resources 55 | true 56 | 57 | 58 | 59 | 60 | 61 | 62 | spigotmc-repo 63 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 64 | 65 | 66 | sonatype 67 | https://oss.sonatype.org/content/groups/public/ 68 | 69 | 70 | 71 | 72 | 73 | org.spigotmc 74 | spigot-api 75 | 1.18-R0.1-SNAPSHOT 76 | provided 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /SimpleCustomEvent/src/main/java/com/senko/simplecustomevent/event/listener/caller/PlayerSneakingHitSheepEventCaller.java: -------------------------------------------------------------------------------- 1 | package com.senko.simplecustomevent.event.listener.caller; 2 | 3 | import com.senko.simplecustomevent.event.PlayerSneakingHitSheepEvent; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.entity.Entity; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.entity.Sheep; 8 | import org.bukkit.event.EventHandler; 9 | import org.bukkit.event.Listener; 10 | import org.bukkit.event.entity.EntityDamageByEntityEvent; 11 | import org.bukkit.event.player.PlayerQuitEvent; 12 | import org.bukkit.event.player.PlayerToggleSneakEvent; 13 | 14 | import java.util.HashSet; 15 | import java.util.Objects; 16 | import java.util.Set; 17 | import java.util.UUID; 18 | 19 | /** 20 | *
21 |  * 玩家蹲下时伤害小样事件 触发器
22 |  *
23 |  * 如何知道 玩家在蹲下来的时候它是否有在击打某只羊?
24 |  * 别无他法,我们只能通过已有的API Event创建一个 监听链,
25 |  * 通过中间值来暂存 “状态” 信息,然后在需要的时候进行判断。
26 |  * 此时要格外注意,如果是用容器这类的数据结构来存放这些中间值,就必须考虑在什么时候去清除被存放的元素,
27 |  * 否则会存在内存泄漏的风险。
28 |  * 
29 | * @author senko 30 | * @date 2022/7/2 19:06 31 | */ 32 | public class PlayerSneakingHitSheepEventCaller implements Listener { 33 | 34 | /** 35 | * 正在蹲着的玩家 36 | * 37 | * 状态信息(标记容器) 38 | */ 39 | private Set playerSet = new HashSet<>(); 40 | 41 | /** 42 | * 监听链第一步:玩家是否正在蹲着 43 | */ 44 | @EventHandler 45 | public void onPlayerSneakingEvent(PlayerToggleSneakEvent event) { 46 | if (event.isSneaking()) { 47 | //正在潜行时 48 | playerSet.add(event.getPlayer().getUniqueId()); 49 | } else { 50 | //不在潜行就尝试删除中间状态 51 | playerSet.remove(event.getPlayer().getUniqueId()); 52 | } 53 | } 54 | 55 | /** 56 | * 监听链第二步:玩家有没有在蹲着的情况下伤害小羊 57 | */ 58 | @EventHandler 59 | public void onPlayerDamageSheepEvent(EntityDamageByEntityEvent event) { 60 | //以前好像遇到过从事件中获取的对象为空的情况,于是我就先空判断了 61 | Entity damager = event.getDamager(); 62 | Entity entity = event.getEntity(); 63 | 64 | //如果damager存在、damger是玩家对象,被害者是羊 65 | if (Objects.nonNull(damager) && 66 | damager instanceof Player && 67 | entity instanceof Sheep) { 68 | 69 | //通过中间值验证玩家是否在蹲着 70 | if (playerSet.contains(damager.getUniqueId())) { 71 | 72 | //取消事件,不对小羊造成伤害 73 | event.setCancelled(true); 74 | 75 | //触发我们的自定义事件,并清空中间状态信息 76 | Bukkit.getPluginManager().callEvent(new PlayerSneakingHitSheepEvent((Player) damager, (Sheep) entity)); 77 | /** 78 | * 一般情况下,都是在监听链的最后才remove | reset状态值, 79 | * 我这里需要玩家在蹲着的时候能重复触发,因此选择在非蹲起的情况下remove | reset状态值。 80 | * 还是那句话,小心内存泄漏! 81 | */ 82 | //playerSet.remove(damager.getUniqueId()); 83 | } 84 | } 85 | } 86 | 87 | @EventHandler 88 | public void onPlayerLogoutEvent(PlayerQuitEvent event) { 89 | //玩家退出时也删除标记 90 | playerSet.remove(event.getPlayer().getUniqueId()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /TextComponentPlugin/src/main/java/com/senko/textcomponentplugin/executor/PrintBossBar.java: -------------------------------------------------------------------------------- 1 | package com.senko.textcomponentplugin.executor; 2 | 3 | import com.senko.textcomponentplugin.TextComponentPlugin; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.boss.BarColor; 6 | import org.bukkit.boss.BarFlag; 7 | import org.bukkit.boss.BarStyle; 8 | import org.bukkit.boss.BossBar; 9 | import org.bukkit.command.Command; 10 | import org.bukkit.command.CommandExecutor; 11 | import org.bukkit.command.CommandSender; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.scheduler.BukkitScheduler; 14 | 15 | import java.util.concurrent.atomic.AtomicInteger; 16 | 17 | /** 18 | * 展示Boss血条 19 | * @author senko 20 | * @date 2022/6/10 8:42 21 | */ 22 | public class PrintBossBar implements CommandExecutor { 23 | 24 | @Override 25 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 26 | if (sender instanceof Player) { 27 | Player player = (Player) sender; 28 | 29 | executeFun(player); 30 | 31 | return true; 32 | } 33 | return false; 34 | } 35 | 36 | /** 37 | * BossBar 也就是打末影龙、凋零时出现的boss血条
38 | * 创建好的bossBar默认不会提示给所有玩家,需要使用 {@link BossBar#addPlayer(Player)}来指定谁会收到提示。
39 | * 同理,不想让玩家看到bossBar就要用到 {@link BossBar#removePlayer(Player)} 或 {@link BossBar#removeAll()}

40 | * 如果只是单纯的将进度条设置为0,玩家依旧能看到进度条。
41 | * 42 | * bossBar.setProgress(0d); // 仍会显示,所以一定要记得手动removePlayer 43 | * 44 | */ 45 | private void executeFun(Player player) { 46 | //通过Bukkit创建BossBar 47 | BossBar bossBar = Bukkit.createBossBar("这是BOSS条的名字", BarColor.GREEN, BarStyle.SEGMENTED_6, BarFlag.DARKEN_SKY); 48 | bossBar.addPlayer(player); //用bossBar对象添加玩家,只有该玩家才能看见boss进度条 49 | 50 | //创建定时任务 51 | TextComponentPlugin plugin = TextComponentPlugin.getInstance(); //拿到插件实例 52 | BukkitScheduler scheduler = Bukkit.getScheduler(); //拿到Schedule线程调度器 53 | AtomicInteger atomicInteger = new AtomicInteger(6); 54 | 55 | //执行异步循环任务,拿到taskId 56 | int taskId = scheduler.scheduleSyncRepeatingTask(plugin, new Runnable() { 57 | @Override 58 | public void run() { 59 | player.sendMessage("当前的boss bar进度:" + atomicInteger.get() * (1d / 6d)); //发送消息 60 | bossBar.setProgress(atomicInteger.decrementAndGet() * (1d / 6d)); //修改进度条进度,实参是double类型 61 | } 62 | }, 0, 2 * 20); //0秒后执行,每2秒执行一次 63 | 64 | //根据taskId取消 repeatAsyncTask的重复执行 65 | scheduler.runTaskLater(plugin, new Runnable() { 66 | @Override 67 | public void run() { 68 | bossBar.removePlayer(player); //移除玩家,不让玩家继续看到进度条 69 | bossBar.setVisible(false); //设置进度条不可见 70 | scheduler.cancelTask(taskId); //取消任务 71 | } 72 | }, 6 * 2 * 20); //6秒后执行 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /PersistentPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | PersistentPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | PersistentPlugin 13 | 14 | 了解数据持久化的另一种方式 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | spigotmc-repo 59 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 60 | 61 | 62 | sonatype 63 | https://oss.sonatype.org/content/groups/public/ 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | com.alibaba 73 | fastjson 74 | 2.0.8 75 | 76 | 77 | 78 | 79 | 80 | org.spigotmc 81 | spigot-api 82 | 1.18-R0.1-SNAPSHOT 83 | provided 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /CustomRecipe/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | CustomRecipe 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | CustomRecipe 13 | 14 | 15 | com.senko 16 | code-learning 17 | 1.0-SNAPSHOT 18 | 19 | 自定义物品合成 20 | 21 | 1.8 22 | UTF-8 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.8.1 31 | 32 | ${java.version} 33 | ${java.version} 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-shade-plugin 39 | 3.2.4 40 | 41 | 42 | package 43 | 44 | shade 45 | 46 | 47 | false 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | src/main/resources 56 | true 57 | 58 | 59 | 60 | 61 | 62 | 63 | spigotmc-repo 64 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 65 | 66 | 67 | sonatype 68 | https://oss.sonatype.org/content/groups/public/ 69 | 70 | 71 | 72 | 73 | 74 | org.spigotmc 75 | spigot-api 76 | 1.18-R0.1-SNAPSHOT 77 | provided 78 | 79 | 80 | com.senko 81 | CustomEventPlugin 82 | 1.0-SNAPSHOT 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /CustomEventPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | CustomEventPlugin 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | CustomEventPlugin 13 | 14 | 15 | com.senko 16 | code-learning 17 | 1.0-SNAPSHOT 18 | 19 | 自定义事件 20 | 21 | 1.8 22 | UTF-8 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.8.1 31 | 32 | ${java.version} 33 | ${java.version} 34 | 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-shade-plugin 39 | 3.2.4 40 | 41 | 42 | package 43 | 44 | shade 45 | 46 | 47 | false 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | src/main/resources 56 | true 57 | 58 | 59 | 60 | 61 | 62 | 63 | spigotmc-repo 64 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 65 | 66 | 67 | sonatype 68 | https://oss.sonatype.org/content/groups/public/ 69 | 70 | 71 | 72 | 73 | 74 | org.jetbrains 75 | annotations 76 | 23.0.0 77 | 78 | 79 | org.spigotmc 80 | spigot-api 81 | 1.18-R0.1-SNAPSHOT 82 | provided 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /CustomRecipe/src/main/java/com/senko/customrecipe/executor/VillagerCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package com.senko.customrecipe.executor; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.Material; 5 | import org.bukkit.World; 6 | import org.bukkit.command.Command; 7 | import org.bukkit.command.CommandExecutor; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Entity; 10 | import org.bukkit.entity.EntityType; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.entity.Villager; 13 | import org.bukkit.inventory.ItemStack; 14 | import org.bukkit.inventory.MerchantRecipe; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.LinkedList; 18 | import java.util.List; 19 | 20 | /** 21 | * 生成一只特殊的村民 22 | */ 23 | public class VillagerCommandExecutor implements CommandExecutor { 24 | /** 25 | * 卧槽,idea可以在注解中插入图片, 真是惊了(看不了就对着函数名ctrl+q)
26 | * 27 | * @param sender 28 | * @param command 29 | * @param label 30 | * @param args 31 | * @return 32 | */ 33 | @Override 34 | public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { 35 | if (label.equalsIgnoreCase("villager")) { 36 | if (sender instanceof Player) { 37 | Player player = (Player) sender; 38 | World world = player.getWorld(); 39 | Location location = player.getLocation(); 40 | 41 | Villager villager = ((Villager) world.spawnEntity(location, EntityType.VILLAGER)); 42 | //修改村民的信息 43 | villager.setAI(false); 44 | villager.setCustomName("奸商YOHO"); 45 | villager.setProfession(Villager.Profession.FARMER); 46 | 47 | 48 | //交易集合 49 | LinkedList recipes = new LinkedList<>(); 50 | 51 | //结果1 52 | ItemStack result1 = new ItemStack(Material.DIAMOND_BLOCK, 2); 53 | //交易1 54 | MerchantRecipe recipe1 = new MerchantRecipe(result1, 3); 55 | //交易需要的材料 集合 56 | LinkedList materials1 = new LinkedList<>(); 57 | ItemStack itemStack = new ItemStack(Material.DIRT, 3); 58 | materials1.add(itemStack); 59 | recipe1.setIngredients(materials1); 60 | //加入配方1 61 | recipes.add(recipe1); 62 | 63 | //结果2 64 | ItemStack result2 = new ItemStack(Material.FEATHER, 2); 65 | MerchantRecipe recipe2 = new MerchantRecipe(result2, 1); 66 | LinkedList materials2 = new LinkedList<>(); 67 | ItemStack m1 = new ItemStack(Material.DIAMOND_BLOCK, 1); 68 | ItemStack m2 = new ItemStack(Material.DIRT, 2); 69 | materials2.add(m1); 70 | materials2.add(m2); 71 | recipe2.setIngredients(materials2); 72 | recipes.add(recipe2); 73 | 74 | villager.setRecipes(recipes); 75 | return true; 76 | } 77 | } 78 | return false; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /MapTutorialPlugin/src/main/java/com/senko/maptutorialplugin/MapTutorialPlugin.java: -------------------------------------------------------------------------------- 1 | package com.senko.maptutorialplugin; 2 | 3 | import com.senko.maptutorialplugin.render.FirstRender; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.Color; 6 | import org.bukkit.Material; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandSender; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.ItemStack; 11 | import org.bukkit.inventory.PlayerInventory; 12 | import org.bukkit.inventory.meta.MapMeta; 13 | import org.bukkit.map.*; 14 | import org.bukkit.plugin.java.JavaPlugin; 15 | 16 | 17 | 18 | public final class MapTutorialPlugin extends JavaPlugin { 19 | 20 | private MapView view; 21 | 22 | { 23 | /** 24 | * 给Map添加View视图 25 | * 26 | * 通过Bukkit的createMap来生成一个MapView 27 | */ 28 | view = Bukkit.createMap(Bukkit.getWorlds().get(0)); 29 | 30 | // 修改视图的信息 31 | view.setScale(MapView.Scale.CLOSEST); 32 | view.setLocked(true); 33 | view.setTrackingPosition(true); // 是否追踪玩家,并显示玩家的cursor true: 显示Cursor,false: 不显示Cursor 34 | view.setUnlimitedTracking(false); // 是否在玩家离开View时仍然标注玩家的cursor true: 标注玩家的cursor,false: 不标注玩家的cursor 35 | 36 | // 删除原有用于渲染世界的Render 37 | view.getRenderers().forEach(view::removeRenderer); 38 | // 添加自己的render 39 | view.addRenderer(new FirstRender()); 40 | } 41 | 42 | @Override 43 | public void onEnable() { 44 | getCommand("get").setExecutor(this); 45 | } 46 | 47 | @Override 48 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 49 | 50 | if (sender instanceof Player) { 51 | Player player = (Player) sender; 52 | PlayerInventory inventory = player.getInventory(); 53 | 54 | // 直接new出Map的物品槽 55 | ItemStack itemStack = new ItemStack(Material.FILLED_MAP, 1); 56 | // 既然是可以展示画面的特殊物品,相比也有个对应的Meta 57 | MapMeta mapMeta = (MapMeta) itemStack.getItemMeta(); 58 | 59 | // 设置meta信息 60 | mapMeta.setLocationName("这是地点:" + player.getLocation().getBlockX() + "," + player.getLocation().getBlockY() + "," + player.getLocation().getBlockZ()); 61 | mapMeta.setColor(Color.fromRGB(255, 0, 0)); 62 | 63 | // 以后获取到的都是相同的View 64 | mapMeta.setMapView(view); 65 | 66 | // 查看默认生成的View信息 67 | int id = view.getId(); 68 | int centerX = view.getCenterX(); 69 | int centerZ = view.getCenterZ(); 70 | MapView.Scale scale = view.getScale(); 71 | sender.sendMessage("视图id:" + id); 72 | sender.sendMessage("视图中心点X坐标:" + centerX); 73 | sender.sendMessage("视图中心点Z坐标:" + centerZ); 74 | sender.sendMessage("视图缩放比例:" + scale); 75 | 76 | // 获取代码创建的Map信息 77 | String locationName = mapMeta.getLocationName(); 78 | sender.sendMessage("locationName: " + locationName); 79 | 80 | Color color = mapMeta.getColor(); 81 | sender.sendMessage("color: " + color); 82 | 83 | 84 | itemStack.setItemMeta(mapMeta); 85 | inventory.addItem(itemStack); 86 | return true; 87 | } 88 | sender.sendMessage("只有玩家才能使用此命令"); 89 | return true; 90 | 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /EasySqlTutorialPlugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.senko 8 | easySqlTutorial 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | EasySqlTutorial 13 | 14 | 上手了解EasySql工具的简单应用 15 | 16 | 1.8 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | org.apache.maven.plugins 24 | maven-compiler-plugin 25 | 3.8.1 26 | 27 | ${java.version} 28 | ${java.version} 29 | 30 | 31 | 32 | org.apache.maven.plugins 33 | maven-shade-plugin 34 | 3.2.4 35 | 36 | 37 | package 38 | 39 | shade 40 | 41 | 42 | false 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | src/main/resources 51 | true 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | EasySQL 61 | GitHub Branch Repository 62 | https://github.com/CarmJos/EasySQL/blob/repo/ 63 | 64 | 65 | 66 | spigotmc-repo 67 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 68 | 69 | 70 | sonatype 71 | https://oss.sonatype.org/content/groups/public/ 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | cc.carm.lib 80 | easysql-hikaricp 81 | 0.4.1 82 | 83 | 84 | 85 | 86 | org.spigotmc 87 | spigot-api 88 | 1.18-R0.1-SNAPSHOT 89 | provided 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /TextComponentPlugin/src/main/java/com/senko/textcomponentplugin/executor/PrintMessage.java: -------------------------------------------------------------------------------- 1 | package com.senko.textcomponentplugin.executor; 2 | 3 | import com.senko.textcomponentplugin.TextComponentPlugin; 4 | import net.md_5.bungee.api.chat.*; 5 | import net.md_5.bungee.api.chat.hover.content.Text; 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.command.Command; 8 | import org.bukkit.command.CommandExecutor; 9 | import org.bukkit.command.CommandSender; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.text.MessageFormat; 13 | import java.util.Objects; 14 | 15 | /** 16 | * ChatColor的基础用法 17 | *

18 | * 与字符串常量拼接, 转换某个字符为颜色代码§ 19 | * 20 | * @author senko 21 | * @date 2022/6/10 7:21 22 | */ 23 | public class PrintMessage implements CommandExecutor { 24 | @Override 25 | public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 26 | if (sender instanceof Player) { 27 | Player player = (Player) sender; 28 | 29 | if (Objects.nonNull(args) && args.length > 0) { 30 | String arg = args[0]; 31 | switch (arg) { 32 | case "1": 33 | fun1(player); 34 | break; 35 | case "2": 36 | fun2(player); 37 | break; 38 | } 39 | return true; 40 | } 41 | return false; 42 | } 43 | return false; 44 | } 45 | 46 | private void fun1(Player player) { 47 | //用法一:ChatColor拼接 48 | String message1 = ChatColor.GOLD + "你好 " + ChatColor.BOLD + ChatColor.RED + "世界"; //转义后实际得到的文本 §6你好 §c世界 49 | //等同于 50 | String message11 = ChatColor.GOLD.toString() + "你好 " + ChatColor.BOLD.toString() + ChatColor.RED.toString() + "世界"; 51 | player.sendMessage(message1); 52 | 53 | //用法二:ChatColor.translateAlternateColorCodes(字符,字符串)转换颜色代码 54 | String message2 = ChatColor.translateAlternateColorCodes('&', "&6你好 &l&c世界"); //转义后实际得到的文本 §6你好 §c世界 55 | player.sendMessage(message2); 56 | } 57 | 58 | /** 59 | * 完全自定义的色彩

60 | * 61 | * {@link ChatColor}枚举所提供的色彩存在一定的限制
62 | * 如果需要自定义色彩,我们可以通过{@link net.md_5.bungee.api.ChatColor#of(String)}传入十六进制颜色码来细分色彩 63 | */ 64 | private void fun2(Player player) { 65 | net.md_5.bungee.api.ChatColor color1 = net.md_5.bungee.api.ChatColor.of("#FCF3CF"); //十六进制的颜色码 66 | net.md_5.bungee.api.ChatColor color2 = net.md_5.bungee.api.ChatColor.of("#F9E79F"); //十六进制的颜色码 67 | net.md_5.bungee.api.ChatColor color3 = net.md_5.bungee.api.ChatColor.of("#F7DC6F"); //... 68 | net.md_5.bungee.api.ChatColor color4 = net.md_5.bungee.api.ChatColor.of("#F4D03F"); 69 | net.md_5.bungee.api.ChatColor color5 = net.md_5.bungee.api.ChatColor.of("#F1C40F"); 70 | net.md_5.bungee.api.ChatColor color6 = net.md_5.bungee.api.ChatColor.of("#D4AC0D"); 71 | 72 | //发送 纯文本 这里也是用了toString()特性 73 | String pureText = MessageFormat.format("{0}大{1}家{2}好{3}啊{4}欢{5}迎", color1, color2, color3, color4, color5, color6); 74 | // pureText.equals(message) == true 75 | String message = color1 + "大" + color2 + "家" + color3 + "好" + color4 + "啊" + color5 + "欢" + color6 + "迎"; 76 | player.sendMessage(pureText + ChatColor.translateAlternateColorCodes('&', " &f sent in pure text form!")); 77 | 78 | 79 | //color1实际对应的颜色代码 80 | TextComponentPlugin.getInstance().getLogger().info( "color1实际对应的颜色代码" + color1); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /BlockPlugin/src/main/java/com/senko/blockplugin/examples/blockdata/BlockDataExample.java: -------------------------------------------------------------------------------- 1 | package com.senko.blockplugin.examples.blockdata; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Location; 5 | import org.bukkit.Material; 6 | import org.bukkit.block.Block; 7 | import org.bukkit.block.BlockFace; 8 | import org.bukkit.block.data.BlockData; 9 | import org.bukkit.block.data.type.Chest; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.util.HashMap; 13 | import java.util.Random; 14 | 15 | /** 16 | * 有关BlockData的基本使用(获取方块信息,以及序列化和反序列化) 17 | * 18 | * @author senko 19 | * @date 2022/7/15 9:49 20 | */ 21 | public class BlockDataExample { 22 | /** 23 | * BlockData在游戏中的体现就是F3键按下后,在屏幕中心右侧显示的Target Block下的那些内容 24 | * (#开头的是注解,因此不会被包含在BlockData里) 25 | * 26 | * 对于存储离散不连续的多个方块的需求,一般都是记录Location,BlockData#getAsString()序列化BlockData,然后通过某种方式存储起来。 27 | * 等需要的时候,再Bukkit#createBlockData(String)反序列化回BlockData,然后用{@link org.bukkit.World#setBlockData(Location, BlockData)}把方块设置在某处。 28 | * (但是BlockData并不是方块的全部,还有BlockState会记负责录方块的详细数据,比如箱子存储了什么、命令方块里的命令是什么等等, 29 | * 所以这种方法只能设置什么位置是什么方块,并不能连同详细数据一同序列化。 30 | * 31 | * 而且Spigot/Bukkit提供的这些修改BlockData的API都很笨重,如果要修改大批量的方块, 32 | * 将会严重增大服务器的性能消耗,除非你抛开Spigot/Bukkit繁重的代码,直接利用底层代码NMS来修改, 33 | * 又或者是使用WorldEdit API(推荐)。 34 | */ 35 | public void doGetAndSetBlockData(Block block, Player player) { 36 | 37 | // TIP: 获取到的BlockData和Location一样,都是对象属性的内存地址,如果要进行修改,请尽量先clone()出副本再针对副本进行修改 38 | BlockData blockData = block.getBlockData(); 39 | 40 | // 比如这里你点击了一个箱子 41 | if (block.getType() == Material.CHEST) { 42 | 43 | Chest chestData = ((Chest) blockData); // 是箱子,就转换成BlockData的子类Chest 44 | player.sendMessage("点击箱子的方向是:" + chestData.getFacing().toString()); // 箱子BlockData所特有的信息 45 | player.sendMessage("点击箱子是否含水:" + (chestData.isWaterlogged() ? "是" : "否")); 46 | player.sendMessage("点击箱子的类型:" + chestData.getType().name()); 47 | player.sendMessage("点击箱子的完整data:" + blockData.getAsString()); // blockData的String形式 48 | //chestData.... 49 | 50 | 51 | // BlockData blockData1 = Bukkit.createBlockData(blockData.getAsString()); // 还有其他方式可以用来(获取)BlockData 52 | // BlockData blockData1 = Bukkit.createBlockData(Material.CHEST); 53 | 54 | changeAnotherBlock(block, chestData); // 利用BlockData去修改其他的方块 55 | 56 | } else { 57 | player.sendMessage("你点击的方块不是箱子,没有方向信息"); 58 | } 59 | 60 | } 61 | 62 | private void changeAnotherBlock(Block curBlock, Chest chestData) { 63 | 64 | Block relative = curBlock.getRelative(chestData.getFacing()); // 获取chest对面的方块 65 | 66 | Chest randomChestData = getRandomChestData(chestData); // 随机修改chest的data。 67 | 68 | relative.setBlockData(randomChestData); // 修改相对方块为随机掉data的箱子 69 | 70 | } 71 | 72 | private Chest getRandomChestData(Chest chestData) { 73 | 74 | // 这里先克隆再修改,是为了不在修改时影响到原方块 75 | Chest clonedChestData = (Chest) chestData.clone(); 76 | 77 | Random random = new Random(); 78 | 79 | int nextType = random.nextInt(Chest.Type.values().length); 80 | clonedChestData.setType(Chest.Type.values()[nextType]); // 通过API修改方块Type 81 | 82 | clonedChestData.setWaterlogged(!clonedChestData.isWaterlogged()); // 修改方块是否含水 83 | 84 | return clonedChestData; // 返回新的BlockData 85 | 86 | } 87 | 88 | } 89 | --------------------------------------------------------------------------------