├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── multilib-bukkit ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── puregero │ └── multilib │ └── bukkit │ ├── BukkitDataStorageImpl.java │ ├── BukkitImpl.java │ └── StoredData.java ├── multilib-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── puregero │ └── multilib │ ├── DataStorageImpl.java │ ├── MultiLibImpl.java │ └── util │ ├── DataStorageCache.java │ └── StringAddition.java ├── multilib-multipaper ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── puregero │ └── multilib │ └── multipaper │ ├── MultiPaperDataStorageImpl.java │ └── MultiPaperImpl.java ├── multilib ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── puregero │ └── multilib │ └── MultiLib.java ├── pom.xml ├── regionized-bukkit ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── puregero │ └── multilib │ └── regionized │ └── bukkit │ ├── BukkitAsyncSchedulerImpl.java │ ├── BukkitEntitySchedulerImpl.java │ ├── BukkitGlobalRegionSchedulerImpl.java │ ├── BukkitRegionizedLibImpl.java │ ├── BukkitRegionizedSchedulerImpl.java │ └── BukkitRegionizedTaskWrapper.java ├── regionized-common ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── github │ └── puregero │ └── multilib │ └── regionized │ ├── AsyncScheduler.java │ ├── EntityScheduler.java │ ├── GlobalRegionScheduler.java │ ├── RegionizedLib.java │ ├── RegionizedScheduler.java │ └── RegionizedTask.java └── regionized-paper ├── pom.xml └── src └── main └── java └── com └── github └── puregero └── multilib └── regionized └── paper ├── PaperAsyncSchedulerImpl.java ├── PaperEntitySchedulerImpl.java ├── PaperGlobalRegionSchedulerImpl.java ├── PaperRegionizedLibImpl.java ├── PaperRegionizedSchedulerImpl.java └── PaperRegionizedTaskWrapper.java /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.sh text eol=lf 4 | gradlew text eol=lf 5 | *.bat text eol=crlf 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | .idea/ 14 | **/*.iml 15 | 16 | 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.nar 21 | *.ear 22 | *.zip 23 | *.tar.gz 24 | *.rar 25 | .gradle 26 | build 27 | target 28 | 29 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 30 | hs_err_pid* 31 | 32 | # Avoid ignoring Gradle wrapper jar file 33 | !gradle-wrapper.jar 34 | 35 | .flattened-pom.xml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 PureGero 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultiLib 2 | MultiLib is a plugin library for interfacing with MultiPaper specific APIs (such as external player detection), with graceful fallbacks maintaining compatibility with both the Bukkit and Spigot API's. 3 | 4 | ## API 5 | All API calls can be found as static util methods in the `MultiLib` class. 6 | 7 | ### Chunks 8 | ```java 9 | boolean isChunkExternal(World world, int cx, int cz); 10 | boolean isChunkExternal(Location location); 11 | boolean isChunkExternal(Entity entity); 12 | boolean isChunkExternal(Block block); 13 | boolean isChunkExternal(Chunk chunk); 14 | boolean isChunkLocal(World world, int cx, int cz); 15 | boolean isChunkLocal(Location location); 16 | boolean isChunkLocal(Entity entity); 17 | boolean isChunkLocal(Block block); 18 | boolean isChunkLocal(Chunk chunk); 19 | ``` 20 | On MultiPaper, an external chunk is one being ticked on an external server, with this server simply containing a copy of the chunk. 21 | On Bukkit, all chunks are local. 22 | 23 | ### Players 24 | ```java 25 | boolean isExternalPlayer(Player player); 26 | boolean isLocalPlayer(Player player); 27 | String getExternalServerName(Player player); 28 | String getLocalServerName(); 29 | ``` 30 | On MultiPaper, an external player is a player connected to another server. 31 | Even if the player is an external player, it may still be in local chunks. 32 | The same can be said for local players, they can be in external chunks. 33 | On Bukkit, all players are local, and the server's local name will always be 34 | "bukkit". 35 | 36 | ```java 37 | @Nullable String getExternalServerName(Player player); 38 | ``` 39 | On MultiPaper, an external player will return the name of the server that the 40 | player is on. A local player has no external server, and will return null. 41 | On Bukkit, all players are local, and will always return null. 42 | 43 | ```java 44 | Collection getAllOnlinePlayers(); 45 | Collection getLocalOnlinePlayers(); 46 | ``` 47 | On MultiPaper, all online players will return all players across all MultiPaper 48 | instances. Local online players will return the players on your single local 49 | MultiPaper instance. 50 | On Bukkit, both these methods return the same collection of players. 51 | 52 | ### Sharing data 53 | ```java 54 | String getData(Player player, String key); 55 | void setData(Player player, String key, String value); 56 | String getPersistentData(Player player, String key); 57 | void setPersistentData(Player player, String key, String value); 58 | ``` 59 | On MultiPaper, data stored with these methods will be available on all servers. 60 | Persistent data will be saved to the disk while non-persistent data will be lost when the player disconnects. 61 | On Bukkit, this data will only be available on the one server, and persistent data will be stored in a yaml file under `multilib-data`. 62 | 63 | ### Notifying other servers 64 | ```java 65 | void on(Plugin plugin, String channel, Consumer callback); 66 | void onString(Plugin plugin, String channel, Consumer callback); 67 | void on(Plugin plugin, String channel, BiConsumer> callbackWithReply); 68 | void onString(Plugin plugin, String channel, BiConsumer> callbackWithReply); 69 | void notify(String channel, byte[] data); 70 | void notify(String channel, String data); 71 | void notify(Chunk chunk, String channel, byte[] data); 72 | void notify(Chunk chunk, String channel, String data); 73 | ``` 74 | On MultiPaper, plugins running on other servers can be notified with a data payload. 75 | You can also notify only certain server with a specified chunk loaded. 76 | On Bukkit, these methods will do nothing as there are no other servers. 77 | 78 | ### Running commands on other servers 79 | ```java 80 | void chatOnOtherServers(Player player, String message); 81 | ``` 82 | On MultiPaper, the specified player will say a chat message (or run a command) 83 | on all other servers. On Bukkit, this method will do nothing as there are no 84 | other servers. 85 | 86 | ### Simple key-value data storage 87 | ```java 88 | MultiLib.getDataStorage(); 89 | CompletableFuture get(String key); 90 | CompletableFuture> list(String keyPrefix); 91 | CompletableFuture set(String key, String value); 92 | CompletableFuture add(String key, int increment); 93 | CompletableFuture add(String key, double increment); 94 | MultiLib.getDataStorage().createCache(Plugin plugin, String keyPrefix); 95 | ``` 96 | An async key-value data storage. This should not be used for large data as the 97 | backing database is in-memory and utilises an inefficient file storage format. 98 | Adding is an atomic operation. Any changes on one server will instantly be 99 | reflected across all other servers. 100 | 101 | ### ShreddedPaper / Folia methods 102 | ```java 103 | RegionizedScheduler getRegionScheduler(); 104 | EntityScheduler getEntityScheduler(Entity entity); 105 | boolean isOwnedByCurrentRegion(Location location); 106 | boolean isOwnedByCurrentRegion(Entity entity); 107 | CompletableFuture getChunkAtAsync(Location location); 108 | CompletableFuture teleportAsync(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause); 109 | ``` 110 | 111 | ## Example Plugin 112 | 113 | ```java 114 | import com.github.puregero.multilib.MultiLib; 115 | 116 | public class MyPlugin extends JavaPlugin { 117 | public void onEnable() { 118 | MultiLib.onString(this, "myplugin:testchannel", (data, reply) -> { 119 | getLogger().info("Received " + data); 120 | reply.accept("myplugin:testchannelreply", "Replying to " + data); 121 | }); 122 | 123 | MultiLib.onString(this, "myplugin:testchannelreply", data -> { 124 | getLogger().info("Received the reply " + data); 125 | }); 126 | 127 | MultiLib.notify("myplugin:testchannel", "Hello!"); 128 | } 129 | 130 | public void doSomething(Player player) { 131 | if (MultiLib.isExternalPlayer(player)) { 132 | getLogger().info("Not our player! Let's not actually do this!"); 133 | } else { 134 | actuallyDoTheSomething(player); 135 | } 136 | } 137 | } 138 | ``` 139 | 140 | ## Build Script Setup 141 | Add the Clojars repository and the MultiLib dependency, then shade and relocate it to your own package. 142 | Relocation helps avoid version conflicts with other plugins using MultiLib. 143 | 144 | ### Gradle 145 | 146 | Repo: 147 | ```groovy 148 | repositories { 149 | maven { 150 | url "https://repo.clojars.org/" 151 | } 152 | } 153 | ``` 154 | 155 | Dependency: 156 | ```groovy 157 | dependencies { 158 | implementation "com.github.puregero:multilib:1.2.4" 159 | } 160 | ``` 161 | 162 | Shadow Jar and Relocate (Groovy Syntax): 163 | ```groovy 164 | plugins { 165 | id "com.gradleup.shadow" version "8.3.5" 166 | // Make sure to always use the latest version (https://plugins.gradle.org/plugin/com.gradleup.shadow) 167 | } 168 | shadowJar { 169 | relocate "com.github.puregero.multilib", "[YOUR PLUGIN PACKAGE].multilib" 170 | } 171 | ``` 172 | 173 | ### Maven 174 | Repo: 175 | ```xml 176 | 177 | 178 | clojars 179 | https://repo.clojars.org 180 | 181 | 182 | ``` 183 | Dependency: 184 | ```xml 185 | 186 | 187 | com.github.puregero 188 | multilib 189 | 1.2.4 190 | compile 191 | 192 | 193 | ``` 194 | 195 | Shade & Relocate: 196 | ```xml 197 | 198 | 199 | 200 | org.apache.maven.plugins 201 | maven-shade-plugin 202 | 3.4.0 203 | 204 | ${project.build.directory}/dependency-reduced-pom.xml 205 | 206 | 207 | com.github.puregero.multilib 208 | [YOUR PLUGIN PACKAGE].multilib 209 | 210 | 211 | 212 | 213 | 214 | package 215 | 216 | shade 217 | 218 | 219 | 220 | 221 | 222 | 223 | ``` 224 | 225 | ## Compiling 226 | MultiPaper is compiled using maven: 227 | ``` 228 | mvn 229 | ``` 230 | 231 | ## License 232 | MultiLib is licensed under the [MIT license](LICENSE) 233 | 234 | ## PaperLib 235 | MultiLib is inspired by PaperMC's [PaperLib](https://github.com/PaperMC/PaperLib) 236 | -------------------------------------------------------------------------------- /multilib-bukkit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.puregero 9 | multilib-parent 10 | ${revision} 11 | 12 | 13 | multilib-bukkit 14 | 15 | 16 | 8 17 | 8 18 | UTF-8 19 | 20 | 21 | 22 | 23 | org.bukkit 24 | bukkit 25 | 1.8.8-R0.1-SNAPSHOT 26 | provided 27 | 28 | 29 | org.yaml 30 | snakeyaml 31 | 2.0 32 | provided 33 | 34 | 35 | com.github.puregero 36 | multilib-common 37 | ${revision} 38 | 39 | 40 | org.jetbrains 41 | annotations 42 | 22.0.0 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /multilib-bukkit/src/main/java/com/github/puregero/multilib/bukkit/BukkitDataStorageImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.bukkit; 2 | 3 | import com.github.puregero.multilib.DataStorageImpl; 4 | import com.github.puregero.multilib.MultiLibImpl; 5 | import com.github.puregero.multilib.util.StringAddition; 6 | import org.bukkit.event.EventHandler; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.server.PluginDisableEvent; 9 | import org.bukkit.plugin.java.JavaPlugin; 10 | import org.yaml.snakeyaml.DumperOptions; 11 | import org.yaml.snakeyaml.LoaderOptions; 12 | import org.yaml.snakeyaml.Yaml; 13 | import org.yaml.snakeyaml.constructor.SafeConstructor; 14 | 15 | import java.io.File; 16 | import java.io.FileInputStream; 17 | import java.io.FileWriter; 18 | import java.io.IOException; 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | import java.util.concurrent.CompletableFuture; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | public class BukkitDataStorageImpl implements DataStorageImpl { 25 | private final BukkitImpl multilib; 26 | private CompletableFuture> yaml; 27 | private CompletableFuture saveFuture; 28 | 29 | public BukkitDataStorageImpl(BukkitImpl multilib) { 30 | this.multilib = multilib; 31 | } 32 | 33 | private synchronized void scheduleSave() { 34 | if (saveFuture == null || saveFuture.isDone()) { 35 | saveFuture = CompletableFuture.runAsync(this::saveYaml, CompletableFuture.delayedExecutor(15, TimeUnit.SECONDS)); 36 | } 37 | } 38 | 39 | private File getFile() { 40 | // Use package name in file to ensure multiple shaded MultiLibs aren't conflicting with eachother 41 | return new File((getClass().getPackageName() + ".").replace("\tcom.github.puregero.multilib.bukkit.".substring(1), "") + "datastorage.yml"); 42 | } 43 | 44 | private synchronized void saveYaml() { 45 | File file = getFile(); 46 | try (FileWriter out = new FileWriter(file)) { 47 | DumperOptions options = new DumperOptions(); 48 | options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); 49 | options.setPrettyFlow(true); 50 | new Yaml(options).dump(yaml.join(), out); 51 | } catch (IOException e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | private synchronized CompletableFuture> loadYaml() { 57 | if (yaml == null) { 58 | yaml = CompletableFuture.supplyAsync(() -> { 59 | registerShutdownHook(); 60 | File file = getFile(); 61 | if (file.isFile()) { 62 | try (FileInputStream in = new FileInputStream(file)) { 63 | return new Yaml(new SafeConstructor(new LoaderOptions())).load(in); 64 | } catch (IOException e) { 65 | e.printStackTrace(); 66 | } 67 | } 68 | return new HashMap<>(); 69 | }); 70 | } 71 | 72 | return yaml; 73 | } 74 | 75 | private void registerShutdownHook() { 76 | JavaPlugin plugin = JavaPlugin.getProvidingPlugin(getClass()); 77 | plugin.getServer().getPluginManager().registerEvents(new Listener() { 78 | @EventHandler 79 | public void onDisable(PluginDisableEvent event) { 80 | if (event.getPlugin() == plugin) { 81 | if (saveFuture != null && !saveFuture.isDone()) { 82 | System.out.println("Saving unsaved datastorage.yaml..."); 83 | saveYaml(); 84 | } 85 | } 86 | } 87 | }, plugin); 88 | } 89 | 90 | @Override 91 | public CompletableFuture get(String key) { 92 | return loadYaml().thenApply(yaml -> (String) yaml.get(key)); 93 | } 94 | 95 | @Override 96 | public CompletableFuture> list(String keyPrefix) { 97 | return loadYaml().thenApply(yaml -> { 98 | Map list = new HashMap<>(); 99 | for (Map.Entry entry : yaml.entrySet()) { 100 | if (entry.getKey() != null && entry.getKey().startsWith(keyPrefix) && entry.getValue() instanceof String) { 101 | list.put(entry.getKey(), (String) entry.getValue()); 102 | } 103 | } 104 | return list; 105 | }); 106 | } 107 | 108 | @Override 109 | public CompletableFuture set(String key, String value) { 110 | return loadYaml().thenApply(yaml -> { 111 | if (value == null) { 112 | yaml.remove(key); 113 | } else { 114 | yaml.put(key, value); 115 | } 116 | scheduleSave(); 117 | return value; 118 | }); 119 | } 120 | 121 | @Override 122 | public CompletableFuture add(String key, String increment) { 123 | return loadYaml().thenApply(yaml -> { 124 | String result = StringAddition.add((String) yaml.get(key), increment); 125 | yaml.put(key, result); 126 | scheduleSave(); 127 | return result; 128 | }); 129 | } 130 | 131 | @Override 132 | public MultiLibImpl getMultiLib() { 133 | return multilib; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /multilib-bukkit/src/main/java/com/github/puregero/multilib/bukkit/BukkitImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.bukkit; 2 | 3 | import com.github.puregero.multilib.DataStorageImpl; 4 | import com.github.puregero.multilib.MultiLibImpl; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.Chunk; 7 | import org.bukkit.Location; 8 | import org.bukkit.World; 9 | import org.bukkit.block.Block; 10 | import org.bukkit.entity.Entity; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.event.EventHandler; 13 | import org.bukkit.event.Listener; 14 | import org.bukkit.event.server.PluginDisableEvent; 15 | import org.bukkit.plugin.Plugin; 16 | import org.bukkit.plugin.java.JavaPlugin; 17 | import org.jetbrains.annotations.NotNull; 18 | 19 | import java.util.ArrayList; 20 | import java.util.Collection; 21 | import java.util.Collections; 22 | import java.util.HashMap; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.UUID; 26 | import java.util.concurrent.ConcurrentHashMap; 27 | import java.util.concurrent.ScheduledThreadPoolExecutor; 28 | import java.util.function.BiConsumer; 29 | import java.util.function.Consumer; 30 | 31 | public class BukkitImpl implements MultiLibImpl { 32 | 33 | private final Map data = new HashMap<>(); 34 | private final ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1); 35 | private final BukkitDataStorageImpl dataStorage = new BukkitDataStorageImpl(this); 36 | private final Map>>> notificationListeners = new ConcurrentHashMap<>(); 37 | 38 | public BukkitImpl() { 39 | JavaPlugin plugin = JavaPlugin.getProvidingPlugin(getClass()); 40 | plugin.getServer().getPluginManager().registerEvents(new Listener() { 41 | @EventHandler 42 | public void onDisable(PluginDisableEvent event) { 43 | if (event.getPlugin() == plugin) { 44 | data.forEach((key, value) -> value.save()); 45 | } 46 | } 47 | }, plugin); 48 | } 49 | 50 | @Override 51 | public boolean isChunkExternal(World world, int cx, int cz) { 52 | return false; 53 | } 54 | 55 | @Override 56 | public boolean isChunkExternal(Location location) { 57 | return false; 58 | } 59 | 60 | @Override 61 | public boolean isChunkExternal(Entity entity) { 62 | return false; 63 | } 64 | 65 | @Override 66 | public boolean isChunkExternal(Block block) { 67 | return false; 68 | } 69 | 70 | @Override 71 | public boolean isChunkExternal(Chunk chunk) { 72 | return false; 73 | } 74 | 75 | @Override 76 | public boolean isChunkLocal(World world, int cx, int cz) { 77 | return true; 78 | } 79 | 80 | @Override 81 | public boolean isChunkLocal(Location location) { 82 | return true; 83 | } 84 | 85 | @Override 86 | public boolean isChunkLocal(Entity entity) { 87 | return true; 88 | } 89 | 90 | @Override 91 | public boolean isChunkLocal(Block block) { 92 | return true; 93 | } 94 | 95 | @Override 96 | public boolean isChunkLocal(Chunk chunk) { 97 | return true; 98 | } 99 | 100 | @Override 101 | public boolean isExternalPlayer(Player player) { 102 | return false; 103 | } 104 | 105 | @Override 106 | public boolean isLocalPlayer(Player player) { 107 | return true; 108 | } 109 | 110 | @Override 111 | public @NotNull String getLocalServerName() { 112 | return "bukkit"; 113 | } 114 | 115 | @Override 116 | public String getExternalServerName(Player player) { 117 | return null; 118 | } 119 | 120 | @Override 121 | public String getData(Player player, String key) { 122 | return data.containsKey(player.getUniqueId()) ? data.get(player.getUniqueId()).getData(player, key) : null; 123 | } 124 | 125 | @Override 126 | public void setData(Player player, String key, String value) { 127 | data.computeIfAbsent(player.getUniqueId(), playerKey -> new StoredData( 128 | player.getUniqueId(), player.getLastPlayed(), scheduler)).setData(player, key, value); 129 | } 130 | 131 | @Override 132 | public String getPersistentData(Player player, String key) { 133 | return data.containsKey(player.getUniqueId()) ? data.get(player.getUniqueId()).getPersistentData(key) : null; 134 | } 135 | 136 | @Override 137 | public void setPersistentData(Player player, String key, String value) { 138 | data.computeIfAbsent(player.getUniqueId(), playerKey -> new StoredData( 139 | player.getUniqueId(), player.getLastPlayed(), scheduler)).setPersistentData(key, value); 140 | } 141 | 142 | @Override 143 | public void on(Plugin plugin, String channel, Consumer callback) { 144 | notificationListeners.computeIfAbsent(channel, key -> new ArrayList<>()).add((data, reply) -> callback.accept(data)); 145 | } 146 | 147 | @Override 148 | public void on(Plugin plugin, String channel, BiConsumer> callbackWithReply) { 149 | notificationListeners.computeIfAbsent(channel, key -> new ArrayList<>()).add(callbackWithReply); 150 | } 151 | 152 | @Override 153 | public void notify(String channel, byte[] data) { 154 | // Do nothing, no other servers exist 155 | } 156 | 157 | @Override 158 | public void notify(Chunk chunk, String channel, byte[] data) { 159 | // Do nothing, no other servers exist 160 | } 161 | 162 | @Override 163 | public void notifyOwningServer(Chunk chunk, String channel, byte[] data) { 164 | // We naturally own this chunk, notify ourself 165 | processNotification(channel, data); 166 | } 167 | 168 | @Override 169 | public void notifyOwningServer(Player player, String channel, byte[] data) { 170 | // We naturally own this chunk, notify ourself 171 | processNotification(channel, data); 172 | } 173 | 174 | private void processNotification(String channel, byte[] data) { 175 | notificationListeners.getOrDefault(channel, Collections.emptyList()).forEach(listener -> listener.accept(data, (replyChannel, replyData) -> { 176 | // We're naturally replying to ourself 177 | processNotification(replyChannel, replyData); 178 | })); 179 | } 180 | 181 | @Override 182 | public void chatOnOtherServers(Player player, String message) { 183 | // Do nothing, no other servers exist 184 | } 185 | 186 | @Override 187 | public Collection getAllOnlinePlayers() { 188 | return Bukkit.getOnlinePlayers(); 189 | } 190 | 191 | @Override 192 | public DataStorageImpl getDataStorage() { 193 | return dataStorage; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /multilib-bukkit/src/main/java/com/github/puregero/multilib/bukkit/StoredData.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.bukkit; 2 | 3 | import org.bukkit.configuration.InvalidConfigurationException; 4 | import org.bukkit.configuration.file.YamlConfiguration; 5 | import org.bukkit.entity.Player; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.UUID; 12 | import java.util.concurrent.ScheduledFuture; 13 | import java.util.concurrent.ScheduledThreadPoolExecutor; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | public class StoredData { 17 | private long lastPlayed; 18 | private final UUID uuid; 19 | private final ScheduledThreadPoolExecutor scheduler; 20 | private final Map data = new HashMap<>(); 21 | private final Map persistentData = new HashMap<>(); 22 | private boolean needsSaving = false; 23 | private ScheduledFuture saveFuture = null; 24 | 25 | public StoredData(UUID uuid, long lastPlayed, ScheduledThreadPoolExecutor scheduler) { 26 | this.uuid = uuid; 27 | this.lastPlayed = lastPlayed; 28 | this.scheduler = scheduler; 29 | 30 | load(); 31 | } 32 | 33 | private File getFile() { 34 | return new File("multilib-data/" + uuid + ".json"); 35 | } 36 | 37 | private void load() { 38 | if (getFile().isFile()) { 39 | try { 40 | YamlConfiguration configuration = new YamlConfiguration(); 41 | 42 | configuration.load(getFile()); 43 | 44 | for (String key : configuration.getKeys(true)) { 45 | persistentData.put(key, configuration.getString(key)); 46 | } 47 | } catch (IOException | InvalidConfigurationException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | } 52 | 53 | public void save() { 54 | if (needsSaving) { 55 | YamlConfiguration configuration = new YamlConfiguration(); 56 | 57 | persistentData.forEach(configuration::set); 58 | 59 | try { 60 | configuration.save(getFile()); 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | } 64 | 65 | needsSaving = false; 66 | } 67 | } 68 | 69 | private void checkIfPlayerChanged(Player player) { 70 | // If the player has changed, that means they disconnected so we need to reset their non-persistent data 71 | if (lastPlayed != player.getLastPlayed()) { 72 | lastPlayed = player.getLastPlayed(); 73 | data.clear(); 74 | } 75 | } 76 | 77 | public String getData(Player player, String key) { 78 | checkIfPlayerChanged(player); 79 | 80 | return data.get(key); 81 | } 82 | 83 | public void setData(Player player, String key, String value) { 84 | checkIfPlayerChanged(player); 85 | 86 | data.put(key, value); 87 | } 88 | 89 | public String getPersistentData(String key) { 90 | return persistentData.get(key); 91 | } 92 | 93 | public void setPersistentData(String key, String value) { 94 | persistentData.put(key, value); 95 | 96 | needsSaving = true; 97 | if (saveFuture == null || saveFuture.isDone() || saveFuture.isCancelled()) { 98 | saveFuture = scheduler.schedule(this::save, 60, TimeUnit.SECONDS); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /multilib-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.puregero 9 | multilib-parent 10 | ${revision} 11 | 12 | 13 | multilib-common 14 | 15 | 16 | 8 17 | 8 18 | UTF-8 19 | 20 | 21 | 22 | 23 | org.bukkit 24 | bukkit 25 | 1.8.8-R0.1-SNAPSHOT 26 | provided 27 | 28 | 29 | org.jetbrains 30 | annotations 31 | 22.0.0 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /multilib-common/src/main/java/com/github/puregero/multilib/DataStorageImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib; 2 | 3 | import com.github.puregero.multilib.util.DataStorageCache; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.CompletableFuture; 8 | 9 | public interface DataStorageImpl { 10 | CompletableFuture get(String key); 11 | 12 | default CompletableFuture get(String key, String def) { 13 | return get(key).thenApply(value -> value != null ? value : def); 14 | } 15 | 16 | default CompletableFuture getLong(String key) { 17 | return get(key).thenApply(Long::parseLong); 18 | } 19 | 20 | default CompletableFuture getLong(String key, long def) { 21 | return getLong(key).exceptionally(throwable -> def); 22 | } 23 | 24 | default CompletableFuture getInt(String key) { 25 | return get(key).thenApply(Integer::parseInt); 26 | } 27 | 28 | default CompletableFuture getInt(String key, int def) { 29 | return getInt(key).exceptionally(throwable -> def); 30 | } 31 | 32 | default CompletableFuture getDouble(String key) { 33 | return get(key).thenApply(Double::parseDouble); 34 | } 35 | 36 | default CompletableFuture getDouble(String key, double def) { 37 | return getDouble(key).exceptionally(throwable -> def); 38 | } 39 | 40 | default CompletableFuture> list() { 41 | return list(null); 42 | } 43 | 44 | CompletableFuture> list(String keyPrefix); 45 | 46 | CompletableFuture set(String key, String value); 47 | 48 | default CompletableFuture set(String key, long value) { 49 | return set(key, Long.toString(value)).thenApply(Long::parseLong); 50 | } 51 | 52 | default CompletableFuture set(String key, int value) { 53 | return set(key, Integer.toString(value)).thenApply(Integer::parseInt); 54 | } 55 | 56 | default CompletableFuture set(String key, double value) { 57 | return set(key, Double.toString(value)).thenApply(Double::parseDouble); 58 | } 59 | 60 | CompletableFuture add(String key, String increment); 61 | 62 | default CompletableFuture add(String key, long increment) { 63 | return add(key, Long.toString(increment)).thenApply(Long::parseLong); 64 | } 65 | 66 | default CompletableFuture add(String key, int increment) { 67 | return add(key, Integer.toString(increment)).thenApply(Integer::parseInt); 68 | } 69 | 70 | default CompletableFuture add(String key, double increment) { 71 | return add(key, Double.toString(increment)).thenApply(Double::parseDouble); 72 | } 73 | 74 | /** 75 | * Creates a cache for the specified key prefix. All keys with the 76 | * specified prefix will be downloaded and cached on the server. When a 77 | * key is modified via this key cache, all other caches on other servers 78 | * will be updated with the modification. 79 | * 80 | * Warning: Any modifications made to keys that are performed outside this 81 | * cache will not be reflected on the cache. 82 | * 83 | * Warning: There must only be one cache per prefix per server. Other 84 | * caches on the same server with the same key prefix will not be updated 85 | * when a key is modified. If you have a cache with the prefix 86 | * "myplugin.subkey", you can not have another cache with the prefix 87 | * "myplugin" as they will conflict. 88 | * 89 | * @param plugin The plugin to register the cache modification listeners 90 | * @param keyPrefix The prefix for keys that should be cached. Eg supply 91 | * "myplugin" if you use keys such as "myplugin.mykey" 92 | * and "myplugin.myotherkey". There must only be one cache 93 | * per prefix per server. An empty string may be supplied 94 | * to cache all keys, but it will conflict with any other 95 | * caches no matter their prefix. 96 | * @return The cache for the specified key prefix 97 | */ 98 | default DataStorageCache createCache(Plugin plugin, String keyPrefix) { 99 | return new DataStorageCache(plugin, keyPrefix, this); 100 | } 101 | 102 | MultiLibImpl getMultiLib(); 103 | } 104 | -------------------------------------------------------------------------------- /multilib-common/src/main/java/com/github/puregero/multilib/MultiLibImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib; 2 | 3 | import org.bukkit.Chunk; 4 | import org.bukkit.Location; 5 | import org.bukkit.World; 6 | import org.bukkit.block.Block; 7 | import org.bukkit.entity.Entity; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.plugin.Plugin; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.nio.charset.StandardCharsets; 14 | import java.util.Collection; 15 | import java.util.function.BiConsumer; 16 | import java.util.function.Consumer; 17 | 18 | public interface MultiLibImpl { 19 | 20 | boolean isChunkExternal(World world, int cx, int cz); 21 | 22 | boolean isChunkExternal(Location location); 23 | 24 | boolean isChunkExternal(Entity entity); 25 | 26 | boolean isChunkExternal(Block block); 27 | 28 | boolean isChunkExternal(Chunk chunk); 29 | 30 | boolean isChunkLocal(World world, int cx, int cz); 31 | 32 | boolean isChunkLocal(Location location); 33 | 34 | boolean isChunkLocal(Entity entity); 35 | 36 | boolean isChunkLocal(Block block); 37 | 38 | boolean isChunkLocal(Chunk chunk); 39 | 40 | boolean isExternalPlayer(Player player); 41 | 42 | boolean isLocalPlayer(Player player); 43 | 44 | @NotNull String getLocalServerName(); 45 | 46 | @Nullable String getExternalServerName(Player player); 47 | 48 | String getData(Player player, String key); 49 | 50 | void setData(Player player, String key, String value); 51 | 52 | String getPersistentData(Player player, String key); 53 | 54 | void setPersistentData(Player player, String key, String value); 55 | 56 | void on(Plugin plugin, String channel, Consumer callback); 57 | 58 | default void onString(Plugin plugin, String channel, Consumer callback) { 59 | on(plugin, channel, bytes -> callback.accept(new String(bytes, StandardCharsets.UTF_8))); 60 | } 61 | 62 | void on(Plugin plugin, String channel, BiConsumer> callbackWithReply); 63 | 64 | default void onString(Plugin plugin, String channel, BiConsumer> callbackWithReply) { 65 | on(plugin, channel, (bytes, reply) -> callbackWithReply.accept( 66 | new String(bytes, StandardCharsets.UTF_8), 67 | (replyChannel, string) -> reply.accept(replyChannel, string.getBytes(StandardCharsets.UTF_8))) 68 | ); 69 | } 70 | 71 | void notify(String channel, byte[] data); 72 | 73 | default void notify(String channel, String data) { 74 | notify(channel, data.getBytes(StandardCharsets.UTF_8)); 75 | } 76 | 77 | void notify(Chunk chunk, String channel, byte[] data); 78 | 79 | default void notify(Chunk chunk, String channel, String data) { 80 | notify(chunk, channel, data.getBytes(StandardCharsets.UTF_8)); 81 | } 82 | 83 | void notifyOwningServer(Chunk chunk, String channel, byte[] data); 84 | 85 | default void notifyOwningServer(Chunk chunk, String channel, String data) { 86 | notifyOwningServer(chunk, channel, data.getBytes(StandardCharsets.UTF_8)); 87 | } 88 | 89 | void notifyOwningServer(Player player, String channel, byte[] data); 90 | 91 | default void notifyOwningServer(Player player, String channel, String data) { 92 | notifyOwningServer(player, channel, data.getBytes(StandardCharsets.UTF_8)); 93 | } 94 | 95 | void chatOnOtherServers(Player player, String message); 96 | 97 | Collection getAllOnlinePlayers(); 98 | 99 | default Collection getLocalOnlinePlayers() { 100 | return getAllOnlinePlayers(); 101 | } 102 | 103 | DataStorageImpl getDataStorage(); 104 | } 105 | -------------------------------------------------------------------------------- /multilib-common/src/main/java/com/github/puregero/multilib/util/DataStorageCache.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.util; 2 | 3 | import com.github.puregero.multilib.DataStorageImpl; 4 | import org.bukkit.plugin.Plugin; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class DataStorageCache { 10 | private static final String CHANNEL = "multilib:datastoragecacheupdate"; 11 | 12 | private final String keyPrefix; 13 | private final DataStorageImpl dataStorage; 14 | private final Map cache = new HashMap<>(); 15 | 16 | public DataStorageCache(Plugin plugin, String keyPrefix, DataStorageImpl dataStorage) { 17 | this.keyPrefix = keyPrefix; 18 | this.dataStorage = dataStorage; 19 | 20 | this.dataStorage.getMultiLib().onString(plugin, CHANNEL, key -> { 21 | if (key.startsWith(keyPrefix)) { 22 | dataStorage.get(key).thenAccept(value -> { 23 | if (value == null) { 24 | cache.remove(key); 25 | } else { 26 | cache.put(key, value); 27 | } 28 | }); 29 | } 30 | }); 31 | 32 | dataStorage.list(keyPrefix).thenAccept(cache::putAll).join(); 33 | } 34 | 35 | private void checkKey(String key) throws IllegalArgumentException { 36 | if (!key.startsWith(keyPrefix)) { 37 | throw new IllegalArgumentException(key + " does not have the prefix " + keyPrefix); 38 | } 39 | } 40 | 41 | public String get(String key) { 42 | checkKey(key); 43 | return cache.get(key); 44 | } 45 | 46 | public String get(String key, String def) { 47 | String value = get(key); 48 | return value != null ? value : def; 49 | } 50 | 51 | public long getLong(String key) throws NumberFormatException { 52 | return Long.parseLong(get(key)); 53 | } 54 | 55 | public long getLong(String key, long def) { 56 | try { 57 | return getLong(key); 58 | } catch (NumberFormatException e) { 59 | return def; 60 | } 61 | } 62 | 63 | public int getInt(String key) throws NumberFormatException { 64 | return Integer.parseInt(get(key)); 65 | } 66 | 67 | public int getInt(String key, int def) { 68 | try { 69 | return getInt(key); 70 | } catch (NumberFormatException e) { 71 | return def; 72 | } 73 | } 74 | 75 | public double getDouble(String key) throws NumberFormatException, NullPointerException { 76 | return Double.parseDouble(get(key)); 77 | } 78 | 79 | public double getDouble(String key, double def) { 80 | try { 81 | return getDouble(key); 82 | } catch (NumberFormatException | NullPointerException e) { 83 | return def; 84 | } 85 | } 86 | 87 | public Map list(String keyPrefix) { 88 | checkKey(keyPrefix); 89 | Map list = new HashMap<>(); 90 | for (Map.Entry entry : cache.entrySet()) { 91 | if (entry.getKey() != null && entry.getKey().startsWith(keyPrefix)) { 92 | list.put(entry.getKey(), entry.getValue()); 93 | } 94 | } 95 | return list; 96 | } 97 | 98 | public void set(String key, String value) { 99 | cache.put(key, value); 100 | dataStorage.set(key, value) 101 | .thenAccept(newValue -> cache.put(key, newValue)) 102 | .thenRun(() -> this.dataStorage.getMultiLib().notify(CHANNEL, key)); 103 | } 104 | 105 | public void set(String key, long value) { 106 | set(key, Long.toString(value)); 107 | } 108 | 109 | public void set(String key, int value) { 110 | set(key, Integer.toString(value)); 111 | } 112 | 113 | public void set(String key, double value) { 114 | set(key, Double.toString(value)); 115 | } 116 | 117 | public void add(String key, String increment) { 118 | cache.put(key, StringAddition.add(key, increment)); 119 | dataStorage.add(key, increment) 120 | .thenAccept(newValue -> cache.put(key, newValue)) 121 | .thenRun(() -> this.dataStorage.getMultiLib().notify(CHANNEL, key)); 122 | } 123 | 124 | public void add(String key, long increment) { 125 | add(key, Long.toString(increment)); 126 | } 127 | 128 | public void add(String key, int increment) { 129 | add(key, Integer.toString(increment)); 130 | } 131 | 132 | public void add(String key, double increment) { 133 | add(key, Double.toString(increment)); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /multilib-common/src/main/java/com/github/puregero/multilib/util/StringAddition.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.util; 2 | 3 | public class StringAddition { 4 | public static String add(String A, String B) { 5 | if (A == null) { 6 | return B; 7 | } 8 | 9 | try { 10 | long a = Long.parseLong(A); 11 | long b = Long.parseLong(B); 12 | return Long.toString(a + b); 13 | } catch (NumberFormatException ignored) {} 14 | 15 | try { 16 | double a = Double.parseDouble(A); 17 | double b = Double.parseDouble(B); 18 | return Double.toString(a + b); 19 | } catch (NumberFormatException ignored) {} 20 | 21 | return B; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /multilib-multipaper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.puregero 9 | multilib-parent 10 | ${revision} 11 | 12 | 13 | multilib-multipaper 14 | 15 | 16 | 17 | com.github.puregero 18 | multipaper-api 19 | 1.20.1-R0.1-SNAPSHOT 20 | provided 21 | 22 | 23 | net.kyori 24 | adventure-api 25 | 4.14.0 26 | provided 27 | 28 | 29 | com.github.puregero 30 | multilib-common 31 | ${revision} 32 | 33 | 34 | org.jetbrains 35 | annotations 36 | 22.0.0 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /multilib-multipaper/src/main/java/com/github/puregero/multilib/multipaper/MultiPaperDataStorageImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.multipaper; 2 | 3 | import com.github.puregero.multilib.DataStorageImpl; 4 | import com.github.puregero.multilib.MultiLibImpl; 5 | import org.bukkit.Bukkit; 6 | 7 | import java.util.Map; 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | public class MultiPaperDataStorageImpl implements DataStorageImpl { 11 | private final MultiPaperImpl multilib; 12 | 13 | public MultiPaperDataStorageImpl(MultiPaperImpl multilib) { 14 | this.multilib = multilib; 15 | } 16 | 17 | @Override 18 | public CompletableFuture get(String key) { 19 | return Bukkit.getMultiPaperDataStorage().get(key); 20 | } 21 | 22 | @Override 23 | public CompletableFuture get(String key, String def) { 24 | return Bukkit.getMultiPaperDataStorage().get(key, def); 25 | } 26 | 27 | @Override 28 | public CompletableFuture getLong(String key) { 29 | return Bukkit.getMultiPaperDataStorage().getLong(key); 30 | } 31 | 32 | @Override 33 | public CompletableFuture getLong(String key, long def) { 34 | return Bukkit.getMultiPaperDataStorage().getLong(key, def); 35 | } 36 | 37 | @Override 38 | public CompletableFuture getInt(String key) { 39 | return Bukkit.getMultiPaperDataStorage().getInt(key); 40 | } 41 | 42 | @Override 43 | public CompletableFuture getInt(String key, int def) { 44 | return Bukkit.getMultiPaperDataStorage().getInt(key, def); 45 | } 46 | 47 | @Override 48 | public CompletableFuture getDouble(String key) { 49 | return Bukkit.getMultiPaperDataStorage().getDouble(key); 50 | } 51 | 52 | @Override 53 | public CompletableFuture getDouble(String key, double def) { 54 | return Bukkit.getMultiPaperDataStorage().getDouble(key, def); 55 | } 56 | 57 | @Override 58 | public CompletableFuture> list() { 59 | return Bukkit.getMultiPaperDataStorage().list(); 60 | } 61 | 62 | @Override 63 | public CompletableFuture> list(String keyPrefix) { 64 | return Bukkit.getMultiPaperDataStorage().list(keyPrefix); 65 | } 66 | 67 | @Override 68 | public CompletableFuture set(String key, String value) { 69 | return Bukkit.getMultiPaperDataStorage().set(key, value); 70 | } 71 | 72 | @Override 73 | public CompletableFuture set(String key, long value) { 74 | return Bukkit.getMultiPaperDataStorage().set(key, value); 75 | } 76 | 77 | @Override 78 | public CompletableFuture set(String key, int value) { 79 | return Bukkit.getMultiPaperDataStorage().set(key, value); 80 | } 81 | 82 | @Override 83 | public CompletableFuture set(String key, double value) { 84 | return Bukkit.getMultiPaperDataStorage().set(key, value); 85 | } 86 | 87 | @Override 88 | public CompletableFuture add(String key, String increment) { 89 | return Bukkit.getMultiPaperDataStorage().add(key, increment); 90 | } 91 | 92 | @Override 93 | public CompletableFuture add(String key, long increment) { 94 | return Bukkit.getMultiPaperDataStorage().add(key, increment); 95 | } 96 | 97 | @Override 98 | public CompletableFuture add(String key, int increment) { 99 | return Bukkit.getMultiPaperDataStorage().add(key, increment); 100 | } 101 | 102 | @Override 103 | public CompletableFuture add(String key, double increment) { 104 | return Bukkit.getMultiPaperDataStorage().add(key, increment); 105 | } 106 | 107 | @Override 108 | public MultiLibImpl getMultiLib() { 109 | return multilib; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /multilib-multipaper/src/main/java/com/github/puregero/multilib/multipaper/MultiPaperImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.multipaper; 2 | 3 | import com.github.puregero.multilib.DataStorageImpl; 4 | import com.github.puregero.multilib.MultiLibImpl; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.Chunk; 7 | import org.bukkit.Location; 8 | import org.bukkit.World; 9 | import org.bukkit.block.Block; 10 | import org.bukkit.entity.Entity; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.plugin.Plugin; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | import java.util.Collection; 16 | import java.util.function.BiConsumer; 17 | import java.util.function.Consumer; 18 | 19 | public class MultiPaperImpl implements MultiLibImpl { 20 | 21 | private final MultiPaperDataStorageImpl dataStorage = new MultiPaperDataStorageImpl(this); 22 | 23 | public MultiPaperImpl() { 24 | try { 25 | Player.class.getMethod("isExternalPlayer"); 26 | } catch (NoSuchMethodException | NoSuchMethodError e) { 27 | throw new IllegalStateException("Not a MultiPaper environment", e); 28 | } 29 | } 30 | 31 | @Override 32 | public boolean isChunkExternal(World world, int cx, int cz) { 33 | return world.isChunkExternal(cx, cz); 34 | } 35 | 36 | @Override 37 | public boolean isChunkExternal(Location location) { 38 | return location.isChunkExternal(); 39 | } 40 | 41 | @Override 42 | public boolean isChunkExternal(Entity entity) { 43 | return entity.isInExternalChunk(); 44 | } 45 | 46 | @Override 47 | public boolean isChunkExternal(Block block) { 48 | return block.isInExternalChunk(); 49 | } 50 | 51 | @Override 52 | public boolean isChunkExternal(Chunk chunk) { 53 | return chunk.isExternalChunk(); 54 | } 55 | 56 | @Override 57 | public boolean isChunkLocal(World world, int cx, int cz) { 58 | return world.isChunkLocal(cx, cz); 59 | } 60 | 61 | @Override 62 | public boolean isChunkLocal(Location location) { 63 | return location.isChunkLocal(); 64 | } 65 | 66 | @Override 67 | public boolean isChunkLocal(Entity entity) { 68 | return entity.isInLocalChunk(); 69 | } 70 | 71 | @Override 72 | public boolean isChunkLocal(Block block) { 73 | return block.isInLocalChunk(); 74 | } 75 | 76 | @Override 77 | public boolean isChunkLocal(Chunk chunk) { 78 | return chunk.isLocalChunk(); 79 | } 80 | 81 | @Override 82 | public boolean isExternalPlayer(Player player) { 83 | return player.isExternalPlayer(); 84 | } 85 | 86 | @Override 87 | public boolean isLocalPlayer(Player player) { 88 | return player.isLocalPlayer(); 89 | } 90 | 91 | @Override 92 | public @NotNull String getLocalServerName() { 93 | return Bukkit.getLocalServerName(); 94 | } 95 | 96 | @Override 97 | public String getExternalServerName(Player player) { 98 | return player.getExternalServerName(); 99 | } 100 | 101 | @Override 102 | public String getData(Player player, String key) { 103 | return player.getData(key); 104 | } 105 | 106 | @Override 107 | public void setData(Player player, String key, String value) { 108 | player.setData(key, value); 109 | } 110 | 111 | @Override 112 | public String getPersistentData(Player player, String key) { 113 | return player.getPersistentData(key); 114 | } 115 | 116 | @Override 117 | public void setPersistentData(Player player, String key, String value) { 118 | player.setPersistentData(key, value); 119 | } 120 | 121 | @Override 122 | public void on(Plugin plugin, String channel, Consumer callback) { 123 | Bukkit.getMultiPaperNotificationManager().on(plugin, channel, callback); 124 | } 125 | 126 | @Override 127 | public void onString(Plugin plugin, String channel, Consumer callback) { 128 | Bukkit.getMultiPaperNotificationManager().onString(plugin, channel, callback); 129 | } 130 | 131 | @Override 132 | public void on(Plugin plugin, String channel, BiConsumer> callbackWithReply) { 133 | Bukkit.getMultiPaperNotificationManager().on(plugin, channel, callbackWithReply); 134 | } 135 | 136 | @Override 137 | public void onString(Plugin plugin, String channel, BiConsumer> callbackWithReply) { 138 | Bukkit.getMultiPaperNotificationManager().onString(plugin, channel, callbackWithReply); 139 | } 140 | 141 | @Override 142 | public void notify(String channel, byte[] data) { 143 | Bukkit.getMultiPaperNotificationManager().notify(channel, data); 144 | } 145 | 146 | @Override 147 | public void notify(String channel, String data) { 148 | Bukkit.getMultiPaperNotificationManager().notify(channel, data); 149 | } 150 | 151 | @Override 152 | public void notify(Chunk chunk, String channel, byte[] data) { 153 | Bukkit.getMultiPaperNotificationManager().notify(chunk, channel, data); 154 | } 155 | 156 | @Override 157 | public void notify(Chunk chunk, String channel, String data) { 158 | Bukkit.getMultiPaperNotificationManager().notify(chunk, channel, data); 159 | } 160 | 161 | @Override 162 | public void notifyOwningServer(Chunk chunk, String channel, byte[] data) { 163 | Bukkit.getMultiPaperNotificationManager().notifyOwningServer(chunk, channel, data); 164 | } 165 | 166 | @Override 167 | public void notifyOwningServer(Chunk chunk, String channel, String data) { 168 | Bukkit.getMultiPaperNotificationManager().notifyOwningServer(chunk, channel, data); 169 | } 170 | 171 | @Override 172 | public void notifyOwningServer(Player player, String channel, byte[] data) { 173 | Bukkit.getMultiPaperNotificationManager().notifyOwningServer(player, channel, data); 174 | } 175 | 176 | @Override 177 | public void notifyOwningServer(Player player, String channel, String data) { 178 | Bukkit.getMultiPaperNotificationManager().notifyOwningServer(player, channel, data); 179 | } 180 | 181 | @Override 182 | public void chatOnOtherServers(Player player, String message) { 183 | player.chatOnOtherServers(message); 184 | } 185 | 186 | @Override 187 | public Collection getAllOnlinePlayers() { 188 | return Bukkit.getAllOnlinePlayers(); 189 | } 190 | 191 | @Override 192 | public Collection getLocalOnlinePlayers() { 193 | return Bukkit.getLocalOnlinePlayers(); 194 | } 195 | 196 | @Override 197 | public DataStorageImpl getDataStorage() { 198 | return dataStorage; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /multilib/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.puregero 9 | multilib-parent 10 | ${revision} 11 | 12 | 13 | multilib 14 | 15 | MultiLib 16 | 17 | 18 | 19 | org.spigotmc 20 | spigot-api 21 | 1.20.6-R0.1-SNAPSHOT 22 | provided 23 | 24 | 25 | org.jetbrains 26 | annotations 27 | 22.0.0 28 | 29 | 30 | 31 | com.github.puregero 32 | multilib-common 33 | ${revision} 34 | compile 35 | 36 | 37 | com.github.puregero 38 | multilib-bukkit 39 | ${revision} 40 | compile 41 | 42 | 43 | com.github.puregero 44 | multilib-multipaper 45 | ${revision} 46 | compile 47 | 48 | 49 | com.github.puregero 50 | regionized-common 51 | ${revision} 52 | compile 53 | 54 | 55 | com.github.puregero 56 | regionized-bukkit 57 | ${revision} 58 | compile 59 | 60 | 61 | com.github.puregero 62 | regionized-paper 63 | ${revision} 64 | compile 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /multilib/src/main/java/com/github/puregero/multilib/MultiLib.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib; 2 | 3 | import com.github.puregero.multilib.bukkit.BukkitImpl; 4 | import com.github.puregero.multilib.multipaper.MultiPaperImpl; 5 | import com.github.puregero.multilib.regionized.AsyncScheduler; 6 | import com.github.puregero.multilib.regionized.EntityScheduler; 7 | import com.github.puregero.multilib.regionized.GlobalRegionScheduler; 8 | import com.github.puregero.multilib.regionized.RegionizedLib; 9 | import com.github.puregero.multilib.regionized.RegionizedScheduler; 10 | import com.github.puregero.multilib.regionized.bukkit.BukkitRegionizedLibImpl; 11 | import com.github.puregero.multilib.regionized.paper.PaperRegionizedLibImpl; 12 | import org.bukkit.Chunk; 13 | import org.bukkit.Location; 14 | import org.bukkit.World; 15 | import org.bukkit.block.Block; 16 | import org.bukkit.entity.Entity; 17 | import org.bukkit.entity.Player; 18 | import org.bukkit.event.player.PlayerTeleportEvent; 19 | import org.bukkit.plugin.Plugin; 20 | import org.jetbrains.annotations.NotNull; 21 | import org.jetbrains.annotations.Nullable; 22 | 23 | import java.util.Collection; 24 | import java.util.concurrent.CompletableFuture; 25 | import java.util.function.BiConsumer; 26 | import java.util.function.Consumer; 27 | 28 | public class MultiLib { 29 | 30 | private static MultiLibImpl multiLib; 31 | private static RegionizedLib regionizedLib; 32 | 33 | private static MultiLibImpl get() { 34 | if (multiLib != null) { 35 | return multiLib; 36 | } 37 | 38 | try { 39 | return multiLib = new MultiPaperImpl(); 40 | } catch (Throwable ignored) { 41 | // Ignored, not MultiPaper environment, fallback to Bukkit 42 | } 43 | 44 | return multiLib = new BukkitImpl(); 45 | } 46 | 47 | private static RegionizedLib getRegionizedLib() { 48 | if (regionizedLib != null) { 49 | return regionizedLib; 50 | } 51 | 52 | try { 53 | return regionizedLib = new PaperRegionizedLibImpl(); 54 | } catch (Throwable ignored) { 55 | // Ignored, not Paper environment, fallback to Bukkit 56 | } 57 | 58 | return regionizedLib = new BukkitRegionizedLibImpl(); 59 | } 60 | 61 | public static boolean isMultiPaper() { 62 | return !(get() instanceof BukkitImpl); 63 | } 64 | 65 | /** 66 | * Returns whether the chunk is running on an external server or not. 67 | * 68 | * @return True if the chunk is an external chunk, or false if the chunk 69 | * is running on this server or if it's unloaded. 70 | */ 71 | public static boolean isChunkExternal(World world, int cx, int cz) { 72 | return get().isChunkExternal(world, cx, cz); 73 | } 74 | 75 | /** 76 | * Returns whether the chunk is running on an external server or not. 77 | * 78 | * @return True if the chunk is an external chunk, or false if the chunk 79 | * is running on this server or if it's unloaded. 80 | */ 81 | public static boolean isChunkExternal(Location location) { 82 | return get().isChunkExternal(location); 83 | } 84 | 85 | /** 86 | * Returns whether the chunk is running on an external server or not. 87 | * 88 | * @return True if the chunk is an external chunk, or false if the chunk 89 | * is running on this server or if it's unloaded. 90 | */ 91 | public static boolean isChunkExternal(Entity entity) { 92 | return get().isChunkExternal(entity); 93 | } 94 | 95 | /** 96 | * Returns whether the chunk is running on an external server or not. 97 | * 98 | * @return True if the chunk is an external chunk, or false if the chunk 99 | * is running on this server or if it's unloaded. 100 | */ 101 | public static boolean isChunkExternal(Block block) { 102 | return get().isChunkExternal(block); 103 | } 104 | 105 | /** 106 | * Returns whether the chunk is running on an external server or not. 107 | * 108 | * @return True if the chunk is an external chunk, or false if the chunk 109 | * is running on this server or if it's unloaded. 110 | */ 111 | public static boolean isChunkExternal(Chunk chunk) { 112 | return get().isChunkExternal(chunk); 113 | } 114 | 115 | /** 116 | * Returns whether the chunk is running on this server or not. 117 | * 118 | * @return True if the chunk is a local chunk, or false if the chunk 119 | * is running on an external server or if it's unloaded. 120 | */ 121 | public static boolean isChunkLocal(World world, int cx, int cz) { 122 | return get().isChunkLocal(world, cx, cz); 123 | } 124 | 125 | /** 126 | * Returns whether the chunk is running on this server or not. 127 | * 128 | * @return True if the chunk is a local chunk, or false if the chunk 129 | * is running on an external server or if it's unloaded. 130 | */ 131 | public static boolean isChunkLocal(Location location) { 132 | return get().isChunkLocal(location); 133 | } 134 | 135 | /** 136 | * Returns whether the chunk is running on this server or not. 137 | * 138 | * @return True if the chunk is a local chunk, or false if the chunk 139 | * is running on an external server or if it's unloaded. 140 | */ 141 | public static boolean isChunkLocal(Entity entity) { 142 | return get().isChunkLocal(entity); 143 | } 144 | 145 | /** 146 | * Returns whether the chunk is running on this server or not. 147 | * 148 | * @return True if the chunk is a local chunk, or false if the chunk 149 | * is running on an external server or if it's unloaded. 150 | */ 151 | public static boolean isChunkLocal(Block block) { 152 | return get().isChunkLocal(block); 153 | } 154 | 155 | /** 156 | * Returns whether the chunk is running on this server or not. 157 | * 158 | * @return True if the chunk is a local chunk, or false if the chunk 159 | * is running on an external server or if it's unloaded. 160 | */ 161 | public static boolean isChunkLocal(Chunk chunk) { 162 | return get().isChunkLocal(chunk); 163 | } 164 | 165 | /** 166 | * Returns whether the player is on an external server or not. 167 | * 168 | * @return True if the player is on an external server. 169 | */ 170 | public static boolean isExternalPlayer(Player player) { 171 | return get().isExternalPlayer(player); 172 | } 173 | 174 | /** 175 | * Returns whether the player is on this server or not. 176 | * 177 | * @return True if the player is on this server. 178 | */ 179 | public static boolean isLocalPlayer(Player player) { 180 | return get().isLocalPlayer(player); 181 | } 182 | 183 | /** 184 | * Get the bungeecord name of this server. 185 | * 186 | * @return the bungeecord name of this server 187 | */ 188 | @NotNull 189 | public static String getLocalServerName() { 190 | return get().getLocalServerName(); 191 | } 192 | 193 | /** 194 | * Get the bungeecord name of the server that this player is on. 195 | * 196 | * @return The bungeecord name of the server the player is on for external 197 | * players, or null for local players. 198 | */ 199 | @Nullable 200 | public static String getExternalServerName(Player player) { 201 | return get().getExternalServerName(player); 202 | } 203 | 204 | /** 205 | * Returns cross-server data that is stored under the specified key. Note 206 | * that all plugins share the same set of keys. This data is 207 | * non-persistent, it will be lost when the player disconnects. 208 | * 209 | * @param key The key the data is stored under. 210 | * @return The data stored under the key, or null if the key isn't set. 211 | */ 212 | public static String getData(Player player, String key) { 213 | return get().getData(player, key); 214 | } 215 | 216 | /** 217 | * Store cross-server data under the specified key. Note that all plugins 218 | * share the same set of keys. This data is non-persistent, it will be 219 | * lost when the player disconnects. 220 | * 221 | * @param key The key to store the data under. 222 | * @param value The data to store under the key. 223 | */ 224 | public static void setData(Player player, String key, String value) { 225 | get().setData(player, key, value); 226 | } 227 | 228 | /** 229 | * Returns cross-server data that is stored under the specified key. Note 230 | * that all plugins share the same set of keys. This data is persistent, 231 | * it will be saved even if the player disconnects. This persistent data is 232 | * saved onto the player's .dat file. 233 | * 234 | * @param key The key the data is stored under. 235 | * @return The data stored under the key, or null if the key isn't set. 236 | */ 237 | public static String getPersistentData(Player player, String key) { 238 | return get().getPersistentData(player, key); 239 | } 240 | 241 | /** 242 | * Store cross-server data under the specified key. Note that all plugins 243 | * share the same set of keys. This data is persistent, it will be saved 244 | * even if the player disconnects. This persistent data is saved onto the 245 | * player's .dat file. 246 | * 247 | * @param key The key to store the data under. 248 | * @param value The data to store under the key. 249 | */ 250 | public static void setPersistentData(Player player, String key, String value) { 251 | get().setPersistentData(player, key, value); 252 | } 253 | 254 | /** 255 | * Listen to notifications sent by other servers. 256 | * 257 | * @param plugin The plugin listening to these notifications 258 | * @param channel The notification channel to listen to 259 | * @param callback A handler for any data received 260 | */ 261 | public static void on(Plugin plugin, String channel, Consumer callback) { 262 | get().on(plugin, channel, callback); 263 | } 264 | 265 | /** 266 | * Listen to notifications sent by other servers. 267 | * 268 | * @param plugin The plugin listening to these notifications 269 | * @param channel The notification channel to listen to 270 | * @param callback A handler for any data received 271 | */ 272 | public static void onString(Plugin plugin, String channel, Consumer callback) { 273 | get().onString(plugin, channel, callback); 274 | } 275 | 276 | /** 277 | * Listen to notifications sent by other servers. 278 | * 279 | * @param plugin The plugin listening to these notifications 280 | * @param channel The notification channel to listen to 281 | * @param callbackWithReply A handler for any data received, and a method to reply to the server on a specified channel 282 | */ 283 | public static void on(Plugin plugin, String channel, BiConsumer> callbackWithReply) { 284 | get().on(plugin, channel, callbackWithReply); 285 | } 286 | 287 | /** 288 | * Listen to notifications sent by other servers. 289 | * 290 | * @param plugin The plugin listening to these notifications 291 | * @param channel The notification channel to listen to 292 | * @param callbackWithReply A handler for any data received, and a method to reply to the server on a specified channel 293 | */ 294 | public static void onString(Plugin plugin, String channel, BiConsumer> callbackWithReply) { 295 | get().onString(plugin, channel, callbackWithReply); 296 | } 297 | 298 | /** 299 | * Notify all other servers. 300 | * 301 | * @param channel The notification channel to notify on 302 | * @param data The data to notify other servers with 303 | */ 304 | public static void notify(String channel, byte[] data) { 305 | get().notify(channel, data); 306 | } 307 | 308 | /** 309 | * Notify all other servers. 310 | * 311 | * @param channel The notification channel to notify on 312 | * @param data The data to notify other servers with 313 | */ 314 | public static void notify(String channel, String data) { 315 | get().notify(channel, data); 316 | } 317 | 318 | /** 319 | * Notify other servers with the specified chunk loaded 320 | * 321 | * @param chunk The chunk that's loaded 322 | * @param channel The notification channel to notify on 323 | * @param data The data to notify other servers with 324 | */ 325 | public static void notify(Chunk chunk, String channel, byte[] data) { 326 | get().notify(chunk, channel, data); 327 | } 328 | 329 | /** 330 | * Notify other servers with the specified chunk loaded 331 | * 332 | * @param chunk The chunk that's loaded 333 | * @param channel The notification channel to notify on 334 | * @param data The data to notify other servers with 335 | */ 336 | public static void notify(Chunk chunk, String channel, String data) { 337 | get().notify(chunk, channel, data); 338 | } 339 | 340 | /** 341 | * Notify the owning server of the specified chunk. 342 | * This chunk must be loaded on this server. 343 | * This will notify this server if this server is the owning server. 344 | * 345 | * @param chunk The loaded chunk with an owning server 346 | * @param channel The notification channel to notify on 347 | * @param data The data to notify other servers with 348 | */ 349 | public static void notifyOwningServer(Chunk chunk, String channel, byte[] data) { 350 | get().notifyOwningServer(chunk, channel, data); 351 | } 352 | 353 | /** 354 | * Notify the owning server of the specified chunk. 355 | * This chunk must be loaded on this server. 356 | * This will notify this server if this server is the owning server. 357 | * 358 | * @param chunk The loaded chunk with an owning server 359 | * @param channel The notification channel to notify on 360 | * @param data The data to notify other servers with 361 | */ 362 | public static void notifyOwningServer(Chunk chunk, String channel, String data) { 363 | get().notifyOwningServer(chunk, channel, data); 364 | } 365 | 366 | /** 367 | * Notify the owning server of the specified player. 368 | * This will notify this server if this server is the owning server. 369 | * 370 | * @param player The player with an owning server 371 | * @param channel The notification channel to notify on 372 | * @param data The data to notify other servers with 373 | */ 374 | public static void notifyOwningServer(Player player, String channel, byte[] data) { 375 | get().notifyOwningServer(player, channel, data); 376 | } 377 | 378 | /** 379 | * Notify the owning server of the specified player. 380 | * This will notify this server if this server is the owning server. 381 | * 382 | * @param player The player with an owning server 383 | * @param channel The notification channel to notify on 384 | * @param data The data to notify other servers with 385 | */ 386 | public static void notifyOwningServer(Player player, String channel, String data) { 387 | get().notifyOwningServer(player, channel, data); 388 | } 389 | 390 | /** 391 | * Says a message (or runs a command) on other servers excluding this one. 392 | * 393 | * @param message The chat message to say 394 | */ 395 | public static void chatOnOtherServers(Player player, String message) { 396 | get().chatOnOtherServers(player, message); 397 | } 398 | 399 | /** 400 | * Returns all online players across all server instances. 401 | * 402 | * @return a view of all online players 403 | */ 404 | public static Collection getAllOnlinePlayers() { 405 | return get().getAllOnlinePlayers(); 406 | } 407 | 408 | /** 409 | * Returns players logged into your single local server instance. 410 | * 411 | * @return a view of players online on your local instance 412 | */ 413 | public static Collection getLocalOnlinePlayers() { 414 | return get().getLocalOnlinePlayers(); 415 | } 416 | 417 | /** 418 | * Gets the multipaper key-value data storage. Accessing this data is 419 | * asynchronous. This storage medium is hosted on the Master instance, 420 | * or a yaml file when using Bukkit. 421 | * 422 | * @return the multipaper data storage 423 | */ 424 | public static DataStorageImpl getDataStorage() { 425 | return get().getDataStorage(); 426 | } 427 | 428 | 429 | /* --- --- --- --- --- --- --- --- --- */ 430 | /* --- --- --- RegionizedLib --- --- --- */ 431 | /* --- --- --- --- --- --- --- --- --- */ 432 | 433 | /** 434 | * Gets a scheduler that can schedule tasks to be run asynchronously 435 | * @return the async scheduler 436 | */ 437 | public static AsyncScheduler getAsyncScheduler() { 438 | return getRegionizedLib().getAsyncScheduler(); 439 | } 440 | 441 | /** 442 | * Gets a scheduler that can schedule tasks to be run on the global thread 443 | * @return the global region scheduler 444 | */ 445 | public static GlobalRegionScheduler getGlobalRegionScheduler() { 446 | return getRegionizedLib().getGlobalRegionScheduler(); 447 | } 448 | 449 | /** 450 | * Gets a scheduler that can schedule tasks to be run on the thread of the 451 | * given region. 452 | * @return the region scheduler 453 | */ 454 | public static RegionizedScheduler getRegionScheduler() { 455 | return getRegionizedLib().getRegionScheduler(); 456 | } 457 | 458 | /** 459 | * Gets a scheduler that can schedule tasks to be run on the thread of the 460 | * given entity. 461 | * @param entity The entity to get the scheduler for 462 | * @return the entity scheduler 463 | */ 464 | public static EntityScheduler getEntityScheduler(Entity entity) { 465 | return getRegionizedLib().getEntityScheduler(entity); 466 | } 467 | 468 | /** 469 | * Returns whether the chunk is owned by the current thread or not. 470 | * 471 | * @return True if the chunk is owned by the current thread, false otherwise. 472 | */ 473 | public static boolean isOwnedByCurrentRegion(World world, int chunkX, int chunkZ) { 474 | return getRegionizedLib().isOwnedByCurrentRegion(world, chunkX, chunkZ); 475 | } 476 | 477 | /** 478 | * Returns whether the location is owned by the current thread or not. 479 | * 480 | * @return True if the location is owned by the current thread, false otherwise. 481 | */ 482 | public static boolean isOwnedByCurrentRegion(Location location) { 483 | return getRegionizedLib().isOwnedByCurrentRegion(location); 484 | } 485 | 486 | /** 487 | * Returns whether the entity is owned by the current thread or not. 488 | * 489 | * @return True if the entity is owned by the current thread, false otherwise. 490 | */ 491 | public static boolean isOwnedByCurrentRegion(Entity entity) { 492 | return getRegionizedLib().isOwnedByCurrentRegion(entity); 493 | } 494 | 495 | /** 496 | * Get the chunk at the specified coordinates asynchronously. 497 | * 498 | * @return a future that will be completed once the chunk is loaded 499 | */ 500 | public static @NotNull CompletableFuture getChunkAtAsync(World world, int chunkX, int chunkZ, boolean gen, boolean urgent) { 501 | return getRegionizedLib().getChunkAtAsync(world, chunkX, chunkZ, gen, urgent); 502 | } 503 | 504 | /** 505 | * Get the chunk at the specified coordinates asynchronously. 506 | * 507 | * @return a future that will be completed once the chunk is loaded 508 | */ 509 | public static @NotNull CompletableFuture getChunkAtAsync(World world, int chunkX, int chunkZ, boolean gen) { 510 | return getRegionizedLib().getChunkAtAsync(world, chunkX, chunkZ, gen); 511 | } 512 | 513 | /** 514 | * Get the chunk at the specified coordinates asynchronously. 515 | * 516 | * @return a future that will be completed once the chunk is loaded 517 | */ 518 | public static @NotNull CompletableFuture getChunkAtAsync(World world, int chunkX, int chunkZ) { 519 | return getRegionizedLib().getChunkAtAsync(world, chunkX, chunkZ); 520 | } 521 | 522 | /** 523 | * Get the chunk at the specified location asynchronously. 524 | * 525 | * @return a future that will be completed once the chunk is loaded 526 | */ 527 | public static @NotNull CompletableFuture getChunkAtAsync(Location location) { 528 | return getRegionizedLib().getChunkAtAsync(location); 529 | } 530 | 531 | /** 532 | * Teleport an entity to a location asynchronously. 533 | * 534 | * @return a future that will be completed once the entity is teleported 535 | */ 536 | public static @NotNull CompletableFuture teleportAsync(Entity entity, Location location) { 537 | return getRegionizedLib().teleportAsync(entity, location); 538 | } 539 | 540 | /** 541 | * Teleport an entity to a location asynchronously. 542 | * 543 | * @return a future that will be completed once the entity is teleported 544 | */ 545 | public static @NotNull CompletableFuture teleportAsync(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) { 546 | return getRegionizedLib().teleportAsync(entity, location, cause); 547 | } 548 | } 549 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.github.puregero 8 | multilib-parent 9 | pom 10 | ${revision} 11 | Plugin Library for interfacing with MultiPaper Specific API's with graceful fallback that maintains Spigot Compatibility, such as External Player Detection. 12 | 13 | 14 | 1.2.4 15 | 16 | 8 17 | 8 18 | UTF-8 19 | 20 | 21 | 22 | 23 | MIT License 24 | http://www.opensource.org/licenses/mit-license.php 25 | 26 | 27 | 28 | 29 | multilib-common 30 | multilib-bukkit 31 | multilib-multipaper 32 | regionized-common 33 | regionized-bukkit 34 | regionized-paper 35 | multilib 36 | 37 | 38 | 39 | 40 | clojars 41 | https://repo.clojars.org 42 | 43 | 44 | spigot-repo 45 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 46 | 47 | 48 | papermc-repo 49 | https://repo.papermc.io/repository/maven-public/ 50 | 51 | 52 | 53 | 54 | 55 | clojars 56 | Clojars repository 57 | https://repo.clojars.org 58 | 59 | 60 | 61 | 62 | clean install 63 | 64 | 65 | maven-compiler-plugin 66 | 67 | ${maven.compiler.source} 68 | ${maven.compiler.target} 69 | 70 | 71 | 72 | org.codehaus.mojo 73 | flatten-maven-plugin 74 | 1.6.0 75 | 76 | 77 | 78 | flatten 79 | process-resources 80 | 81 | flatten 82 | 83 | 84 | 85 | 86 | flatten.clean 87 | clean 88 | 89 | clean 90 | 91 | 92 | 93 | 94 | 95 | maven-install-plugin 96 | 97 | .flattened-pom.xml 98 | 99 | 100 | 101 | maven-source-plugin 102 | 103 | 104 | attach-sources 105 | deploy 106 | jar-no-fork 107 | 108 | 109 | 110 | 111 | maven-javadoc-plugin 112 | 113 | 114 | attach-javadocs 115 | deploy 116 | jar 117 | 118 | 119 | 120 | 121 | 122 | maven-deploy-plugin 123 | 124 | .flattened-pom.xml 125 | 126 | 127 | 128 | deploy 129 | deploy 130 | deploy 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /regionized-bukkit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.puregero 9 | multilib-parent 10 | ${revision} 11 | 12 | 13 | regionized-bukkit 14 | 15 | 16 | 17 | org.bukkit 18 | bukkit 19 | 1.8.8-R0.1-SNAPSHOT 20 | provided 21 | 22 | 23 | com.github.puregero 24 | regionized-common 25 | ${revision} 26 | 27 | 28 | org.jetbrains 29 | annotations 30 | 22.0.0 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /regionized-bukkit/src/main/java/com/github/puregero/multilib/regionized/bukkit/BukkitAsyncSchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.bukkit; 2 | 3 | import com.github.puregero.multilib.regionized.AsyncScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import org.bukkit.plugin.Plugin; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.function.Consumer; 9 | 10 | public class BukkitAsyncSchedulerImpl implements AsyncScheduler { 11 | 12 | @Override 13 | public @NotNull RegionizedTask runNow(@NotNull Plugin plugin, @NotNull Consumer task) { 14 | return new BukkitRegionizedTaskWrapper(plugin, task).scheduleAsync(); 15 | } 16 | 17 | @Override 18 | public @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, long delayTicks) { 19 | return new BukkitRegionizedTaskWrapper(plugin, task, delayTicks).scheduleAsync(); 20 | } 21 | 22 | @Override 23 | public @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelayTicks, long periodTicks) { 24 | return new BukkitRegionizedTaskWrapper(plugin, task, initialDelayTicks, periodTicks).scheduleAsync(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /regionized-bukkit/src/main/java/com/github/puregero/multilib/regionized/bukkit/BukkitEntitySchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.bukkit; 2 | 3 | import com.github.puregero.multilib.regionized.EntityScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import org.bukkit.entity.Entity; 6 | import org.bukkit.plugin.Plugin; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.function.Consumer; 11 | 12 | public class BukkitEntitySchedulerImpl implements EntityScheduler { 13 | 14 | private final Entity entity; 15 | 16 | public BukkitEntitySchedulerImpl(Entity entity) { 17 | this.entity = entity; 18 | } 19 | 20 | private Entity getEntity() { 21 | return this.entity; 22 | } 23 | 24 | private void handle(RegionizedTask regionizedTask, Consumer runnable, Runnable retired) { 25 | if (getEntity() == null || getEntity().isDead() || !getEntity().isValid()) { 26 | regionizedTask.cancel(); 27 | if (retired != null) { 28 | retired.run(); 29 | } 30 | return; 31 | } 32 | 33 | runnable.accept(regionizedTask); 34 | } 35 | 36 | @Override 37 | public boolean execute(@NotNull Plugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) { 38 | new BukkitRegionizedTaskWrapper(plugin, regionizedTask -> handle(regionizedTask, t -> run.run(), retired), delay).schedule(); 39 | return true; 40 | } 41 | 42 | @Override 43 | public @Nullable RegionizedTask run(@NotNull Plugin plugin, @NotNull Consumer task, @Nullable Runnable retired) { 44 | return new BukkitRegionizedTaskWrapper(plugin, regionizedTask -> handle(regionizedTask, task, retired)).schedule(); 45 | } 46 | 47 | @Override 48 | public @Nullable RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, @Nullable Runnable retired, long delayTicks) { 49 | return new BukkitRegionizedTaskWrapper(plugin, regionizedTask -> handle(regionizedTask, task, retired), delayTicks).schedule(); 50 | } 51 | 52 | @Override 53 | public @Nullable RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) { 54 | return new BukkitRegionizedTaskWrapper(plugin, regionizedTask -> handle(regionizedTask, task, retired), initialDelayTicks, periodTicks).schedule(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /regionized-bukkit/src/main/java/com/github/puregero/multilib/regionized/bukkit/BukkitGlobalRegionSchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.bukkit; 2 | 3 | import com.github.puregero.multilib.regionized.GlobalRegionScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.plugin.Plugin; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.function.Consumer; 10 | 11 | public class BukkitGlobalRegionSchedulerImpl implements GlobalRegionScheduler { 12 | 13 | @Override 14 | public void execute(@NotNull Plugin plugin, @NotNull Runnable run) { 15 | Bukkit.getScheduler().runTask(plugin, run); 16 | } 17 | 18 | @Override 19 | public @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull Consumer task) { 20 | return new BukkitRegionizedTaskWrapper(plugin, task).schedule(); 21 | } 22 | 23 | @Override 24 | public @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, long delayTicks) { 25 | return new BukkitRegionizedTaskWrapper(plugin, task, delayTicks).schedule(); 26 | } 27 | 28 | @Override 29 | public @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelayTicks, long periodTicks) { 30 | return new BukkitRegionizedTaskWrapper(plugin, task, initialDelayTicks, periodTicks).schedule(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /regionized-bukkit/src/main/java/com/github/puregero/multilib/regionized/bukkit/BukkitRegionizedLibImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.bukkit; 2 | 3 | import com.github.puregero.multilib.regionized.AsyncScheduler; 4 | import com.github.puregero.multilib.regionized.EntityScheduler; 5 | import com.github.puregero.multilib.regionized.GlobalRegionScheduler; 6 | import com.github.puregero.multilib.regionized.RegionizedLib; 7 | import com.github.puregero.multilib.regionized.RegionizedScheduler; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.Chunk; 10 | import org.bukkit.Location; 11 | import org.bukkit.World; 12 | import org.bukkit.entity.Entity; 13 | import org.bukkit.event.player.PlayerTeleportEvent; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | import java.util.concurrent.CompletableFuture; 17 | 18 | public class BukkitRegionizedLibImpl implements RegionizedLib { 19 | 20 | private final BukkitAsyncSchedulerImpl asyncScheduler = new BukkitAsyncSchedulerImpl(); 21 | private final BukkitGlobalRegionSchedulerImpl globalRegionScheduler = new BukkitGlobalRegionSchedulerImpl(); 22 | private final BukkitRegionizedSchedulerImpl regionScheduler = new BukkitRegionizedSchedulerImpl(); 23 | 24 | @Override 25 | public AsyncScheduler getAsyncScheduler() { 26 | return this.asyncScheduler; 27 | } 28 | 29 | @Override 30 | public GlobalRegionScheduler getGlobalRegionScheduler() { 31 | return this.globalRegionScheduler; 32 | } 33 | 34 | @Override 35 | public RegionizedScheduler getRegionScheduler() { 36 | return this.regionScheduler; 37 | } 38 | 39 | @Override 40 | public EntityScheduler getEntityScheduler(Entity entity) { 41 | return new BukkitEntitySchedulerImpl(entity); 42 | } 43 | 44 | @Override 45 | public boolean isOwnedByCurrentRegion(World world, int chunkX, int chunkZ) { 46 | return Bukkit.isPrimaryThread(); 47 | } 48 | 49 | @Override 50 | public @NotNull CompletableFuture getChunkAtAsync(World world, int x, int z, boolean gen, boolean urgent) { 51 | return CompletableFuture.completedFuture(world.getChunkAt(x, z)); 52 | } 53 | 54 | @Override 55 | public @NotNull CompletableFuture teleportAsync(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) { 56 | return CompletableFuture.completedFuture(entity.teleport(location, cause)); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /regionized-bukkit/src/main/java/com/github/puregero/multilib/regionized/bukkit/BukkitRegionizedSchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.bukkit; 2 | 3 | import com.github.puregero.multilib.regionized.RegionizedScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.World; 7 | import org.bukkit.plugin.Plugin; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.function.Consumer; 11 | 12 | public class BukkitRegionizedSchedulerImpl implements RegionizedScheduler { 13 | 14 | @Override 15 | public void execute(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run) { 16 | Bukkit.getScheduler().runTask(plugin, run); 17 | } 18 | 19 | @Override 20 | public @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task) { 21 | return new BukkitRegionizedTaskWrapper(plugin, task).schedule(); 22 | } 23 | 24 | @Override 25 | public @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task, long delayTicks) { 26 | return new BukkitRegionizedTaskWrapper(plugin, task, delayTicks).schedule(); 27 | } 28 | 29 | @Override 30 | public @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task, long initialDelayTicks, long periodTicks) { 31 | return new BukkitRegionizedTaskWrapper(plugin, task, initialDelayTicks, periodTicks).schedule(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /regionized-bukkit/src/main/java/com/github/puregero/multilib/regionized/bukkit/BukkitRegionizedTaskWrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.bukkit; 2 | 3 | import com.github.puregero.multilib.regionized.RegionizedTask; 4 | import org.bukkit.plugin.Plugin; 5 | import org.bukkit.scheduler.BukkitTask; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.concurrent.atomic.AtomicReference; 9 | import java.util.function.Consumer; 10 | 11 | public class BukkitRegionizedTaskWrapper implements RegionizedTask, Runnable { 12 | 13 | private final Plugin plugin; 14 | private final Consumer task; 15 | private final long initialDelayTicks; 16 | private final long periodTicks; 17 | private BukkitTask bukkitTask; 18 | 19 | private final AtomicReference executionState = new AtomicReference<>(ExecutionState.IDLE); 20 | 21 | public BukkitRegionizedTaskWrapper(Plugin plugin, Consumer task) { 22 | this(plugin, task, 0); 23 | } 24 | 25 | public BukkitRegionizedTaskWrapper(Plugin plugin, Consumer task, long delayTicks) { 26 | this(plugin, task, delayTicks, -1); 27 | } 28 | 29 | public BukkitRegionizedTaskWrapper(Plugin plugin, Consumer task, long initialDelayTicks, long periodTicks) { 30 | this.plugin = plugin; 31 | this.task = task; 32 | this.initialDelayTicks = initialDelayTicks; 33 | this.periodTicks = periodTicks; 34 | } 35 | 36 | @Override 37 | public void run() { 38 | if (!this.executionState.compareAndSet(ExecutionState.IDLE, ExecutionState.RUNNING)) { 39 | return; 40 | } 41 | 42 | try { 43 | this.task.accept(this); 44 | } finally { 45 | if (this.isRepeatingTask()) { 46 | this.executionState.compareAndSet(ExecutionState.RUNNING, ExecutionState.IDLE); 47 | } else { 48 | this.executionState.compareAndSet(ExecutionState.RUNNING, ExecutionState.FINISHED); 49 | } 50 | this.executionState.compareAndSet(ExecutionState.CANCELLED_RUNNING, ExecutionState.CANCELLED); 51 | } 52 | } 53 | 54 | public BukkitRegionizedTaskWrapper schedule() { 55 | if (this.isRepeatingTask()) { 56 | this.bukkitTask = this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, this, this.initialDelayTicks, this.periodTicks); 57 | } else { 58 | this.bukkitTask = this.plugin.getServer().getScheduler().runTaskLater(this.plugin, this, this.initialDelayTicks); 59 | } 60 | 61 | return this; 62 | } 63 | 64 | public BukkitRegionizedTaskWrapper scheduleAsync() { 65 | if (this.isRepeatingTask()) { 66 | this.bukkitTask = this.plugin.getServer().getScheduler().runTaskTimerAsynchronously(this.plugin, this, this.initialDelayTicks, this.periodTicks); 67 | } else { 68 | this.bukkitTask = this.plugin.getServer().getScheduler().runTaskLaterAsynchronously(this.plugin, this, this.initialDelayTicks); 69 | } 70 | 71 | return this; 72 | } 73 | 74 | @Override 75 | public @NotNull Plugin getOwningPlugin() { 76 | return this.plugin; 77 | } 78 | 79 | @Override 80 | public boolean isRepeatingTask() { 81 | return this.periodTicks > 0; 82 | } 83 | 84 | @Override 85 | public @NotNull RegionizedTask.CancelledState cancel() { 86 | if (executionState.compareAndSet(ExecutionState.IDLE, ExecutionState.CANCELLED)) { 87 | this.bukkitTask.cancel(); 88 | return CancelledState.CANCELLED_BY_CALLER; 89 | } 90 | if (executionState.compareAndSet(ExecutionState.RUNNING, ExecutionState.CANCELLED_RUNNING)) { 91 | this.bukkitTask.cancel(); 92 | if (isRepeatingTask()) { 93 | return CancelledState.NEXT_RUNS_CANCELLED; 94 | } else { 95 | return CancelledState.RUNNING; 96 | } 97 | } 98 | switch (executionState.get()) { 99 | case IDLE: 100 | case RUNNING: 101 | this.bukkitTask.cancel(); 102 | executionState.set(ExecutionState.CANCELLED); 103 | return CancelledState.CANCELLED_BY_CALLER; 104 | case CANCELLED: 105 | return CancelledState.CANCELLED_ALREADY; 106 | case CANCELLED_RUNNING: 107 | return CancelledState.NEXT_RUNS_CANCELLED_ALREADY; 108 | case FINISHED: 109 | return CancelledState.ALREADY_EXECUTED; 110 | default: 111 | throw new IllegalStateException("Unknown execution state: " + executionState.get()); 112 | } 113 | } 114 | 115 | @Override 116 | public @NotNull RegionizedTask.ExecutionState getExecutionState() { 117 | return this.executionState.get(); 118 | } 119 | 120 | @Override 121 | public boolean isCancelled() { 122 | return this.getExecutionState() == ExecutionState.CANCELLED || this.getExecutionState() == ExecutionState.CANCELLED_RUNNING; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /regionized-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.puregero 9 | multilib-parent 10 | ${revision} 11 | 12 | 13 | regionized-common 14 | 15 | 16 | 17 | org.bukkit 18 | bukkit 19 | 1.8.8-R0.1-SNAPSHOT 20 | provided 21 | 22 | 23 | com.github.puregero 24 | multilib-common 25 | ${revision} 26 | 27 | 28 | org.jetbrains 29 | annotations 30 | 22.0.0 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /regionized-common/src/main/java/com/github/puregero/multilib/regionized/AsyncScheduler.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized; 2 | 3 | import org.bukkit.plugin.Plugin; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.function.Consumer; 8 | 9 | public interface AsyncScheduler { 10 | 11 | /** 12 | * Schedules the specified task to be executed asynchronously immediately. 13 | * @param plugin Plugin which owns the specified task. 14 | * @param task Specified task. 15 | * @return The {@link RegionizedTask} that represents the scheduled task. 16 | */ 17 | @NotNull RegionizedTask runNow(@NotNull Plugin plugin, @NotNull Consumer task); 18 | 19 | /** 20 | * Schedules the specified task to be executed asynchronously after the time delay has passed. 21 | * @param plugin Plugin which owns the specified task. 22 | * @param task Specified task. 23 | * @param delayTicks The delay, in ticks. 24 | * @return The {@link RegionizedTask} that represents the scheduled task. 25 | */ 26 | @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, long delayTicks); 27 | 28 | /** 29 | * Schedules the specified task to be executed asynchronously after the initial delay has passed, 30 | * and then periodically executed with the specified period. 31 | * @param plugin Plugin which owns the specified task. 32 | * @param task Specified task. 33 | * @param initialDelayTicks The initial delay, in ticks. 34 | * @param periodTicks The period, in ticks. 35 | * @return The {@link RegionizedTask} that represents the scheduled task. 36 | */ 37 | @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, 38 | long initialDelayTicks, long periodTicks); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /regionized-common/src/main/java/com/github/puregero/multilib/regionized/EntityScheduler.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized; 2 | 3 | import org.bukkit.plugin.Plugin; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.function.Consumer; 8 | 9 | public interface EntityScheduler { 10 | /** 11 | * Schedules a task with the given delay. If the task failed to schedule because the scheduler is retired (entity 12 | * removed), then returns {@code false}. Otherwise, either the run callback will be invoked after the specified delay, 13 | * or the retired callback will be invoked if the scheduler is retired. 14 | * Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove 15 | * other entities, load chunks, load worlds, modify ticket levels, etc. 16 | * 17 | *

18 | * It is guaranteed that the run and retired callback are invoked on the region which owns the entity. 19 | *

20 | * @param run The callback to run after the specified delay, may not be null. 21 | * @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null. 22 | * @param delay The delay in ticks before the run callback is invoked. Any value less-than 1 is treated as 1. 23 | * @return {@code true} if the task was scheduled, which means that either the run function or the retired function 24 | * will be invoked (but never both), or {@code false} indicating neither the run nor retired function will be invoked 25 | * since the scheduler has been retired. 26 | */ 27 | boolean execute(@NotNull Plugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay); 28 | 29 | /** 30 | * Schedules a task to execute on the next tick. If the task failed to schedule because the scheduler is retired (entity 31 | * removed), then returns {@code null}. Otherwise, either the task callback will be invoked after the specified delay, 32 | * or the retired callback will be invoked if the scheduler is retired. 33 | * Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove 34 | * other entities, load chunks, load worlds, modify ticket levels, etc. 35 | * 36 | *

37 | * It is guaranteed that the task and retired callback are invoked on the region which owns the entity. 38 | *

39 | * @param plugin The plugin that owns the task 40 | * @param task The task to execute 41 | * @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null. 42 | * @return The {@link RegionizedTask} that represents the scheduled task, or {@code null} if the entity has been removed. 43 | */ 44 | @Nullable RegionizedTask run(@NotNull Plugin plugin, @NotNull Consumer task, 45 | @Nullable Runnable retired); 46 | 47 | /** 48 | * Schedules a task with the given delay. If the task failed to schedule because the scheduler is retired (entity 49 | * removed), then returns {@code null}. Otherwise, either the task callback will be invoked after the specified delay, 50 | * or the retired callback will be invoked if the scheduler is retired. 51 | * Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove 52 | * other entities, load chunks, load worlds, modify ticket levels, etc. 53 | * 54 | *

55 | * It is guaranteed that the task and retired callback are invoked on the region which owns the entity. 56 | *

57 | * @param plugin The plugin that owns the task 58 | * @param task The task to execute 59 | * @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null. 60 | * @param delayTicks The delay, in ticks. 61 | * @return The {@link RegionizedTask} that represents the scheduled task, or {@code null} if the entity has been removed. 62 | */ 63 | @Nullable RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, 64 | @Nullable Runnable retired, long delayTicks); 65 | 66 | /** 67 | * Schedules a repeating task with the given delay and period. If the task failed to schedule because the scheduler 68 | * is retired (entity removed), then returns {@code null}. Otherwise, either the task callback will be invoked after 69 | * the specified delay, or the retired callback will be invoked if the scheduler is retired. 70 | * Note that the retired callback is invoked in critical code, so it should not attempt to remove the entity, remove 71 | * other entities, load chunks, load worlds, modify ticket levels, etc. 72 | * 73 | *

74 | * It is guaranteed that the task and retired callback are invoked on the region which owns the entity. 75 | *

76 | * @param plugin The plugin that owns the task 77 | * @param task The task to execute 78 | * @param retired Retire callback to run if the entity is retired before the run callback can be invoked, may be null. 79 | * @param initialDelayTicks The initial delay, in ticks. 80 | * @param periodTicks The period, in ticks. 81 | * @return The {@link RegionizedTask} that represents the scheduled task, or {@code null} if the entity has been removed. 82 | */ 83 | @Nullable RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, 84 | @Nullable Runnable retired, long initialDelayTicks, long periodTicks); 85 | 86 | } 87 | -------------------------------------------------------------------------------- /regionized-common/src/main/java/com/github/puregero/multilib/regionized/GlobalRegionScheduler.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized; 2 | 3 | import org.bukkit.plugin.Plugin; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.function.Consumer; 7 | 8 | public interface GlobalRegionScheduler { 9 | 10 | /** 11 | * Schedules a task to be executed on the global region. 12 | * @param plugin The plugin that owns the task 13 | * @param run The task to execute 14 | */ 15 | void execute(@NotNull Plugin plugin, @NotNull Runnable run); 16 | 17 | /** 18 | * Schedules a task to be executed on the global region on the next tick. 19 | * @param plugin The plugin that owns the task 20 | * @param task The task to execute 21 | * @return The {@link RegionizedTask} that represents the scheduled task. 22 | */ 23 | @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull Consumer task); 24 | 25 | /** 26 | * Schedules a task to be executed on the global region after the specified delay in ticks. 27 | * @param plugin The plugin that owns the task 28 | * @param task The task to execute 29 | * @param delayTicks The delay, in ticks. 30 | * @return The {@link RegionizedTask} that represents the scheduled task. 31 | */ 32 | @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, long delayTicks); 33 | 34 | /** 35 | * Schedules a repeating task to be executed on the global region after the initial delay with the 36 | * specified period. 37 | * @param plugin The plugin that owns the task 38 | * @param task The task to execute 39 | * @param initialDelayTicks The initial delay, in ticks. 40 | * @param periodTicks The period, in ticks. 41 | * @return The {@link RegionizedTask} that represents the scheduled task. 42 | */ 43 | @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, 44 | long initialDelayTicks, long periodTicks); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /regionized-common/src/main/java/com/github/puregero/multilib/regionized/RegionizedLib.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized; 2 | 3 | import org.bukkit.Chunk; 4 | import org.bukkit.Location; 5 | import org.bukkit.World; 6 | import org.bukkit.entity.Entity; 7 | import org.bukkit.event.player.PlayerTeleportEvent; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.concurrent.CompletableFuture; 11 | 12 | public interface RegionizedLib { 13 | 14 | AsyncScheduler getAsyncScheduler(); 15 | 16 | GlobalRegionScheduler getGlobalRegionScheduler(); 17 | 18 | RegionizedScheduler getRegionScheduler(); 19 | 20 | EntityScheduler getEntityScheduler(Entity entity); 21 | 22 | boolean isOwnedByCurrentRegion(World world, int chunkX, int chunkZ); 23 | 24 | default boolean isOwnedByCurrentRegion(Location location) { 25 | return isOwnedByCurrentRegion(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); 26 | } 27 | 28 | default boolean isOwnedByCurrentRegion(Entity entity) { 29 | return isOwnedByCurrentRegion(entity.getLocation()); 30 | } 31 | 32 | @NotNull CompletableFuture getChunkAtAsync(World world, int x, int z, boolean gen, boolean urgent); 33 | 34 | default @NotNull CompletableFuture getChunkAtAsync(World world, int x, int z, boolean gen) { 35 | return getChunkAtAsync(world, x, z, gen, false); 36 | } 37 | 38 | default @NotNull CompletableFuture getChunkAtAsync(World world, int x, int z) { 39 | return getChunkAtAsync(world, x, z, true); 40 | } 41 | 42 | default @NotNull CompletableFuture getChunkAtAsync(Location location) { 43 | return getChunkAtAsync(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); 44 | } 45 | 46 | default @NotNull CompletableFuture teleportAsync(Entity entity, Location location) { 47 | return teleportAsync(entity, location, PlayerTeleportEvent.TeleportCause.PLUGIN); 48 | } 49 | 50 | @NotNull CompletableFuture teleportAsync(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /regionized-common/src/main/java/com/github/puregero/multilib/regionized/RegionizedScheduler.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.World; 5 | import org.bukkit.plugin.Plugin; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.function.Consumer; 9 | 10 | public interface RegionizedScheduler { 11 | 12 | /** 13 | * Schedules a task to be executed on the region which owns the location. 14 | * 15 | * @param plugin The plugin that owns the task 16 | * @param world The world of the region that owns the task 17 | * @param chunkX The chunk X coordinate of the region that owns the task 18 | * @param chunkZ The chunk Z coordinate of the region that owns the task 19 | * @param run The task to execute 20 | */ 21 | void execute(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run); 22 | 23 | /** 24 | * Schedules a task to be executed on the region which owns the location. 25 | * 26 | * @param plugin The plugin that owns the task 27 | * @param location The location at which the region executing should own 28 | * @param run The task to execute 29 | */ 30 | default void execute(@NotNull Plugin plugin, @NotNull Location location, @NotNull Runnable run) { 31 | this.execute(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, run); 32 | } 33 | 34 | /** 35 | * Schedules a task to be executed on the region which owns the location on the next tick. 36 | * 37 | * @param plugin The plugin that owns the task 38 | * @param world The world of the region that owns the task 39 | * @param chunkX The chunk X coordinate of the region that owns the task 40 | * @param chunkZ The chunk Z coordinate of the region that owns the task 41 | * @param task The task to execute 42 | * @return The {@link RegionizedTask} that represents the scheduled task. 43 | */ 44 | @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task); 45 | 46 | /** 47 | * Schedules a task to be executed on the region which owns the location on the next tick. 48 | * 49 | * @param plugin The plugin that owns the task 50 | * @param location The location at which the region executing should own 51 | * @param task The task to execute 52 | * @return The {@link RegionizedTask} that represents the scheduled task. 53 | */ 54 | default @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer task) { 55 | return this.run(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task); 56 | } 57 | 58 | /** 59 | * Schedules a task to be executed on the region which owns the location after the specified delay in ticks. 60 | * 61 | * @param plugin The plugin that owns the task 62 | * @param world The world of the region that owns the task 63 | * @param chunkX The chunk X coordinate of the region that owns the task 64 | * @param chunkZ The chunk Z coordinate of the region that owns the task 65 | * @param task The task to execute 66 | * @param delayTicks The delay, in ticks. 67 | * @return The {@link RegionizedTask} that represents the scheduled task. 68 | */ 69 | @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task, 70 | long delayTicks); 71 | 72 | /** 73 | * Schedules a task to be executed on the region which owns the location after the specified delay in ticks. 74 | * 75 | * @param plugin The plugin that owns the task 76 | * @param location The location at which the region executing should own 77 | * @param task The task to execute 78 | * @param delayTicks The delay, in ticks. 79 | * @return The {@link RegionizedTask} that represents the scheduled task. 80 | */ 81 | default @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer task, 82 | long delayTicks) { 83 | return this.runDelayed(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, delayTicks); 84 | } 85 | 86 | /** 87 | * Schedules a repeating task to be executed on the region which owns the location after the initial delay with the 88 | * specified period. 89 | * 90 | * @param plugin The plugin that owns the task 91 | * @param world The world of the region that owns the task 92 | * @param chunkX The chunk X coordinate of the region that owns the task 93 | * @param chunkZ The chunk Z coordinate of the region that owns the task 94 | * @param task The task to execute 95 | * @param initialDelayTicks The initial delay, in ticks. 96 | * @param periodTicks The period, in ticks. 97 | * @return The {@link RegionizedTask} that represents the scheduled task. 98 | */ 99 | @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task, 100 | long initialDelayTicks, long periodTicks); 101 | 102 | /** 103 | * Schedules a repeating task to be executed on the region which owns the location after the initial delay with the 104 | * specified period. 105 | * 106 | * @param plugin The plugin that owns the task 107 | * @param location The location at which the region executing should own 108 | * @param task The task to execute 109 | * @param initialDelayTicks The initial delay, in ticks. 110 | * @param periodTicks The period, in ticks. 111 | * @return The {@link RegionizedTask} that represents the scheduled task. 112 | */ 113 | default @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer task, 114 | long initialDelayTicks, long periodTicks) { 115 | return this.runAtFixedRate(plugin, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4, task, initialDelayTicks, periodTicks); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /regionized-common/src/main/java/com/github/puregero/multilib/regionized/RegionizedTask.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized; 2 | 3 | import org.bukkit.plugin.Plugin; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public interface RegionizedTask { 7 | 8 | /** 9 | * Returns the plugin that scheduled this task. 10 | * @return the plugin that scheduled this task. 11 | */ 12 | @NotNull 13 | Plugin getOwningPlugin(); 14 | 15 | /** 16 | * Returns whether this task executes on a fixed period, as opposed to executing only once. 17 | * @return whether this task executes on a fixed period, as opposed to executing only once. 18 | */ 19 | boolean isRepeatingTask(); 20 | 21 | /** 22 | * Attempts to cancel this task, returning the result of the attempt. In all cases, if the task is currently 23 | * being executed no attempt is made to halt the task, however any executions in the future are halted. 24 | * @return the result of the cancellation attempt. 25 | */ 26 | @NotNull 27 | RegionizedTask.CancelledState cancel(); 28 | 29 | /** 30 | * Returns the current execution state of this task. 31 | * @return the current execution state of this task. 32 | */ 33 | @NotNull 34 | RegionizedTask.ExecutionState getExecutionState(); 35 | 36 | /** 37 | * Returns whether the current execution state is {@link RegionizedTask.ExecutionState#CANCELLED} or {@link RegionizedTask.ExecutionState#CANCELLED_RUNNING}. 38 | * @return whether the current execution state is {@link RegionizedTask.ExecutionState#CANCELLED} or {@link RegionizedTask.ExecutionState#CANCELLED_RUNNING}. 39 | */ 40 | default boolean isCancelled() { 41 | final RegionizedTask.ExecutionState state = this.getExecutionState(); 42 | return state == RegionizedTask.ExecutionState.CANCELLED || state == RegionizedTask.ExecutionState.CANCELLED_RUNNING; 43 | } 44 | 45 | /** 46 | * Represents the result of attempting to cancel a task. 47 | */ 48 | enum CancelledState { 49 | /** 50 | * The task (repeating or not) has been successfully cancelled by the caller thread. The task is not executing 51 | * currently, and it will not begin execution in the future. 52 | */ 53 | CANCELLED_BY_CALLER, 54 | /** 55 | * The task (repeating or not) is already cancelled. The task is not executing currently, and it will not 56 | * begin execution in the future. 57 | */ 58 | CANCELLED_ALREADY, 59 | 60 | /** 61 | * The task is not a repeating task, and could not be cancelled because the task is being executed. 62 | */ 63 | RUNNING, 64 | /** 65 | * The task is not a repeating task, and could not be cancelled because the task has already finished execution. 66 | */ 67 | ALREADY_EXECUTED, 68 | 69 | /** 70 | * The caller thread successfully stopped future executions of a repeating task, but the task is currently 71 | * being executed. 72 | */ 73 | NEXT_RUNS_CANCELLED, 74 | 75 | /** 76 | * The repeating task's future executions are cancelled already, but the task is currently 77 | * being executed. 78 | */ 79 | NEXT_RUNS_CANCELLED_ALREADY, 80 | } 81 | 82 | /** 83 | * Represents the current execution state of the task. 84 | */ 85 | enum ExecutionState { 86 | /** 87 | * The task is currently not executing, but may begin execution in the future. 88 | */ 89 | IDLE, 90 | 91 | /** 92 | * The task is currently executing. 93 | */ 94 | RUNNING, 95 | 96 | /** 97 | * The task is not repeating, and the task finished executing. 98 | */ 99 | FINISHED, 100 | 101 | /** 102 | * The task is not executing and will not begin execution in the future. If this task is not repeating, then 103 | * this task was never executed. 104 | */ 105 | CANCELLED, 106 | 107 | /** 108 | * The task is repeating and currently executing, but future executions are cancelled and will not occur. 109 | */ 110 | CANCELLED_RUNNING; 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /regionized-paper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.github.puregero 9 | multilib-parent 10 | ${revision} 11 | 12 | 13 | 14 | 17 15 | 17 16 | 17 | 18 | regionized-paper 19 | 20 | 21 | 22 | io.papermc.paper 23 | paper-api 24 | 1.20.6-R0.1-SNAPSHOT 25 | provided 26 | 27 | 28 | com.github.puregero 29 | regionized-common 30 | ${revision} 31 | 32 | 33 | org.jetbrains 34 | annotations 35 | 22.0.0 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /regionized-paper/src/main/java/com/github/puregero/multilib/regionized/paper/PaperAsyncSchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.paper; 2 | 3 | import com.github.puregero.multilib.regionized.AsyncScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import org.bukkit.plugin.Plugin; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | import java.util.function.Consumer; 10 | 11 | public class PaperAsyncSchedulerImpl implements AsyncScheduler { 12 | 13 | private final io.papermc.paper.threadedregions.scheduler.AsyncScheduler scheduler; 14 | 15 | public PaperAsyncSchedulerImpl(io.papermc.paper.threadedregions.scheduler.AsyncScheduler scheduler) { 16 | this.scheduler = scheduler; 17 | } 18 | 19 | @Override 20 | public @NotNull RegionizedTask runNow(@NotNull Plugin plugin, @NotNull Consumer task) { 21 | return new PaperRegionizedTaskWrapper(scheduler.runNow(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)))); 22 | } 23 | 24 | @Override 25 | public @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, long delayTicks) { 26 | return new PaperRegionizedTaskWrapper(scheduler.runDelayed(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), delayTicks * 50, TimeUnit.MILLISECONDS)); 27 | } 28 | 29 | @Override 30 | public @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelayTicks, long periodTicks) { 31 | return new PaperRegionizedTaskWrapper(scheduler.runAtFixedRate(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), initialDelayTicks * 50, periodTicks * 50, TimeUnit.MILLISECONDS)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /regionized-paper/src/main/java/com/github/puregero/multilib/regionized/paper/PaperEntitySchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.paper; 2 | 3 | import com.github.puregero.multilib.regionized.EntityScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import org.bukkit.plugin.Plugin; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.function.Consumer; 10 | 11 | public class PaperEntitySchedulerImpl implements EntityScheduler { 12 | 13 | private final io.papermc.paper.threadedregions.scheduler.EntityScheduler scheduler; 14 | 15 | public PaperEntitySchedulerImpl(io.papermc.paper.threadedregions.scheduler.EntityScheduler scheduler) { 16 | this.scheduler = scheduler; 17 | } 18 | 19 | @Override 20 | public boolean execute(@NotNull Plugin plugin, @NotNull Runnable run, @Nullable Runnable retired, long delay) { 21 | return scheduler.execute(plugin, run, retired, delay); 22 | } 23 | 24 | @Override 25 | public @Nullable RegionizedTask run(@NotNull Plugin plugin, @NotNull Consumer task, @Nullable Runnable retired) { 26 | return new PaperRegionizedTaskWrapper(scheduler.run(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), retired)); 27 | } 28 | 29 | @Override 30 | public @Nullable RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, @Nullable Runnable retired, long delayTicks) { 31 | return new PaperRegionizedTaskWrapper(scheduler.runDelayed(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), retired, delayTicks)); 32 | } 33 | 34 | @Override 35 | public @Nullable RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, @Nullable Runnable retired, long initialDelayTicks, long periodTicks) { 36 | return new PaperRegionizedTaskWrapper(scheduler.runAtFixedRate(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), retired, initialDelayTicks, periodTicks)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /regionized-paper/src/main/java/com/github/puregero/multilib/regionized/paper/PaperGlobalRegionSchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.paper; 2 | 3 | import com.github.puregero.multilib.regionized.GlobalRegionScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import org.bukkit.plugin.Plugin; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.function.Consumer; 9 | 10 | public class PaperGlobalRegionSchedulerImpl implements GlobalRegionScheduler { 11 | 12 | private final io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler scheduler; 13 | 14 | public PaperGlobalRegionSchedulerImpl(io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler scheduler) { 15 | this.scheduler = scheduler; 16 | } 17 | 18 | @Override 19 | public void execute(@NotNull Plugin plugin, @NotNull Runnable run) { 20 | scheduler.execute(plugin, run); 21 | } 22 | 23 | @Override 24 | public @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull Consumer task) { 25 | return new PaperRegionizedTaskWrapper(scheduler.run(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)))); 26 | } 27 | 28 | @Override 29 | public @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Consumer task, long delayTicks) { 30 | return new PaperRegionizedTaskWrapper(scheduler.runDelayed(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), delayTicks)); 31 | } 32 | 33 | @Override 34 | public @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Consumer task, long initialDelayTicks, long periodTicks) { 35 | return new PaperRegionizedTaskWrapper(scheduler.runAtFixedRate(plugin, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), initialDelayTicks, periodTicks)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /regionized-paper/src/main/java/com/github/puregero/multilib/regionized/paper/PaperRegionizedLibImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.paper; 2 | 3 | import com.github.puregero.multilib.regionized.AsyncScheduler; 4 | import com.github.puregero.multilib.regionized.EntityScheduler; 5 | import com.github.puregero.multilib.regionized.GlobalRegionScheduler; 6 | import com.github.puregero.multilib.regionized.RegionizedLib; 7 | import com.github.puregero.multilib.regionized.RegionizedScheduler; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.Chunk; 10 | import org.bukkit.Location; 11 | import org.bukkit.Server; 12 | import org.bukkit.World; 13 | import org.bukkit.entity.Entity; 14 | import org.bukkit.event.player.PlayerTeleportEvent; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | public class PaperRegionizedLibImpl implements RegionizedLib { 20 | 21 | private final PaperAsyncSchedulerImpl asyncScheduler = new PaperAsyncSchedulerImpl(Bukkit.getAsyncScheduler()); 22 | private final PaperGlobalRegionSchedulerImpl globalRegionScheduler = new PaperGlobalRegionSchedulerImpl(Bukkit.getGlobalRegionScheduler()); 23 | private final PaperRegionizedSchedulerImpl regionScheduler = new PaperRegionizedSchedulerImpl(Bukkit.getRegionScheduler()); 24 | 25 | public PaperRegionizedLibImpl() { 26 | try { 27 | Entity.class.getMethod("getScheduler"); 28 | Server.class.getMethod("getRegionScheduler"); 29 | Server.class.getMethod("getGlobalRegionScheduler"); 30 | Server.class.getMethod("getAsyncScheduler"); 31 | } catch (NoSuchMethodException | NoSuchMethodError e) { 32 | throw new IllegalStateException("Not a Folia-compatible Paper environment", e); 33 | } 34 | } 35 | 36 | @Override 37 | public AsyncScheduler getAsyncScheduler() { 38 | return asyncScheduler; 39 | } 40 | 41 | @Override 42 | public GlobalRegionScheduler getGlobalRegionScheduler() { 43 | return globalRegionScheduler; 44 | } 45 | 46 | @Override 47 | public RegionizedScheduler getRegionScheduler() { 48 | return regionScheduler; 49 | } 50 | 51 | @Override 52 | public EntityScheduler getEntityScheduler(Entity entity) { 53 | return new PaperEntitySchedulerImpl(entity.getScheduler()); 54 | } 55 | 56 | @Override 57 | public boolean isOwnedByCurrentRegion(World world, int chunkX, int chunkZ) { 58 | return Bukkit.isOwnedByCurrentRegion(world, chunkX, chunkZ); 59 | } 60 | 61 | @Override 62 | public boolean isOwnedByCurrentRegion(Location location) { 63 | return Bukkit.isOwnedByCurrentRegion(location); 64 | } 65 | 66 | @Override 67 | public boolean isOwnedByCurrentRegion(Entity entity) { 68 | return Bukkit.isOwnedByCurrentRegion(entity); 69 | } 70 | 71 | @Override 72 | public @NotNull CompletableFuture getChunkAtAsync(World world, int x, int z, boolean gen, boolean urgent) { 73 | return world.getChunkAtAsync(x, z, gen, urgent); 74 | } 75 | 76 | @Override 77 | public @NotNull CompletableFuture getChunkAtAsync(World world, int x, int z, boolean gen) { 78 | return world.getChunkAtAsync(x, z, gen); 79 | } 80 | 81 | @Override 82 | public @NotNull CompletableFuture getChunkAtAsync(World world, int x, int z) { 83 | return world.getChunkAtAsync(x, z); 84 | } 85 | 86 | @Override 87 | public @NotNull CompletableFuture getChunkAtAsync(Location location) { 88 | return location.getWorld().getChunkAtAsync(location); 89 | } 90 | 91 | @Override 92 | public @NotNull CompletableFuture teleportAsync(Entity entity, Location location, PlayerTeleportEvent.TeleportCause cause) { 93 | return entity.teleportAsync(location, cause); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /regionized-paper/src/main/java/com/github/puregero/multilib/regionized/paper/PaperRegionizedSchedulerImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.paper; 2 | 3 | import com.github.puregero.multilib.regionized.RegionizedScheduler; 4 | import com.github.puregero.multilib.regionized.RegionizedTask; 5 | import io.papermc.paper.threadedregions.scheduler.RegionScheduler; 6 | import org.bukkit.Location; 7 | import org.bukkit.World; 8 | import org.bukkit.plugin.Plugin; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.function.Consumer; 12 | 13 | public class PaperRegionizedSchedulerImpl implements RegionizedScheduler { 14 | 15 | private final RegionScheduler scheduler; 16 | 17 | public PaperRegionizedSchedulerImpl(RegionScheduler scheduler) { 18 | this.scheduler = scheduler; 19 | } 20 | 21 | 22 | @Override 23 | public void execute(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Runnable run) { 24 | scheduler.execute(plugin, world, chunkX, chunkZ, run); 25 | } 26 | 27 | @Override 28 | public void execute(@NotNull Plugin plugin, @NotNull Location location, @NotNull Runnable run) { 29 | scheduler.execute(plugin, location, run); 30 | } 31 | 32 | @Override 33 | public @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task) { 34 | return new PaperRegionizedTaskWrapper(scheduler.run(plugin, world, chunkX, chunkZ, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)))); 35 | } 36 | 37 | @Override 38 | public @NotNull RegionizedTask run(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer task) { 39 | return new PaperRegionizedTaskWrapper(scheduler.run(plugin, location, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)))); 40 | } 41 | 42 | @Override 43 | public @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task, long delayTicks) { 44 | return new PaperRegionizedTaskWrapper(scheduler.runDelayed(plugin, world, chunkX, chunkZ, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), delayTicks)); 45 | } 46 | 47 | @Override 48 | public @NotNull RegionizedTask runDelayed(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer task, long delayTicks) { 49 | return new PaperRegionizedTaskWrapper(scheduler.runDelayed(plugin, location, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), delayTicks)); 50 | } 51 | 52 | @Override 53 | public @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull World world, int chunkX, int chunkZ, @NotNull Consumer task, long initialDelayTicks, long periodTicks) { 54 | return new PaperRegionizedTaskWrapper(scheduler.runAtFixedRate(plugin, world, chunkX, chunkZ, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), initialDelayTicks, periodTicks)); 55 | } 56 | 57 | @Override 58 | public @NotNull RegionizedTask runAtFixedRate(@NotNull Plugin plugin, @NotNull Location location, @NotNull Consumer task, long initialDelayTicks, long periodTicks) { 59 | return new PaperRegionizedTaskWrapper(scheduler.runAtFixedRate(plugin, location, scheduledTask -> task.accept(new PaperRegionizedTaskWrapper(scheduledTask)), initialDelayTicks, periodTicks)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /regionized-paper/src/main/java/com/github/puregero/multilib/regionized/paper/PaperRegionizedTaskWrapper.java: -------------------------------------------------------------------------------- 1 | package com.github.puregero.multilib.regionized.paper; 2 | 3 | import com.github.puregero.multilib.regionized.RegionizedTask; 4 | import io.papermc.paper.threadedregions.scheduler.ScheduledTask; 5 | import org.bukkit.plugin.Plugin; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public class PaperRegionizedTaskWrapper implements RegionizedTask { 9 | 10 | private final ScheduledTask scheduledTask; 11 | 12 | public PaperRegionizedTaskWrapper(ScheduledTask scheduledTask) { 13 | this.scheduledTask = scheduledTask; 14 | } 15 | 16 | @Override 17 | public @NotNull Plugin getOwningPlugin() { 18 | return this.scheduledTask.getOwningPlugin(); 19 | } 20 | 21 | @Override 22 | public boolean isRepeatingTask() { 23 | return this.scheduledTask.isRepeatingTask(); 24 | } 25 | 26 | @Override 27 | public @NotNull RegionizedTask.CancelledState cancel() { 28 | return from(this.scheduledTask.cancel()); 29 | } 30 | 31 | @Override 32 | public @NotNull RegionizedTask.ExecutionState getExecutionState() { 33 | return from(this.scheduledTask.getExecutionState()); 34 | } 35 | 36 | @Override 37 | public boolean isCancelled() { 38 | return this.scheduledTask.isCancelled(); 39 | } 40 | 41 | private static RegionizedTask.CancelledState from(ScheduledTask.CancelledState state) { 42 | return switch (state) { 43 | case CANCELLED_BY_CALLER -> CancelledState.CANCELLED_BY_CALLER; 44 | case CANCELLED_ALREADY -> CancelledState.CANCELLED_ALREADY; 45 | case RUNNING -> CancelledState.RUNNING; 46 | case ALREADY_EXECUTED -> CancelledState.ALREADY_EXECUTED; 47 | case NEXT_RUNS_CANCELLED -> CancelledState.NEXT_RUNS_CANCELLED; 48 | case NEXT_RUNS_CANCELLED_ALREADY -> CancelledState.NEXT_RUNS_CANCELLED_ALREADY; 49 | }; 50 | } 51 | 52 | private static RegionizedTask.ExecutionState from(ScheduledTask.ExecutionState state) { 53 | return switch (state) { 54 | case IDLE -> ExecutionState.IDLE; 55 | case RUNNING -> ExecutionState.RUNNING; 56 | case FINISHED -> ExecutionState.FINISHED; 57 | case CANCELLED -> ExecutionState.CANCELLED; 58 | case CANCELLED_RUNNING -> ExecutionState.CANCELLED_RUNNING; 59 | }; 60 | } 61 | } 62 | --------------------------------------------------------------------------------