001package net.tnemc.plugincore.paper.impl; 002 003/* 004 * The New Plugin Core 005 * Copyright (C) 2022 - 2024 Daniel "creatorfromhell" Vidmar 006 * 007 * This program is free software: you can redistribute it and/or modify 008 * it under the terms of the GNU Affero General Public License as published by 009 * the Free Software Foundation, either version 3 of the License, or 010 * (at your option) any later version. 011 * 012 * This program is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 015 * GNU Affero General Public License for more details. 016 * 017 * You should have received a copy of the GNU Affero General Public License 018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 019 */ 020 021import net.tnemc.item.AbstractItemStack; 022import net.tnemc.item.paper.PaperCalculationsProvider; 023import net.tnemc.item.paper.PaperItemStack; 024import net.tnemc.plugincore.PluginCore; 025import net.tnemc.plugincore.core.compatibility.CmdSource; 026import net.tnemc.plugincore.core.compatibility.PlayerProvider; 027import net.tnemc.plugincore.core.compatibility.ProxyProvider; 028import net.tnemc.plugincore.core.compatibility.ServerConnector; 029import net.tnemc.plugincore.core.compatibility.WorldProvider; 030import net.tnemc.plugincore.core.compatibility.helper.CraftingRecipe; 031import net.tnemc.plugincore.core.compatibility.scheduler.SchedulerProvider; 032import net.tnemc.plugincore.paper.PaperPluginCore; 033import net.tnemc.plugincore.paper.hook.PAPIParser; 034import net.tnemc.plugincore.paper.impl.scheduler.PaperScheduler; 035import org.bukkit.Bukkit; 036import org.bukkit.ChatColor; 037import org.bukkit.Material; 038import org.bukkit.NamespacedKey; 039import org.bukkit.OfflinePlayer; 040import org.bukkit.World; 041import org.bukkit.entity.Player; 042import org.bukkit.inventory.ItemStack; 043import org.bukkit.inventory.ShapedRecipe; 044import org.bukkit.inventory.ShapelessRecipe; 045import org.jetbrains.annotations.NotNull; 046import org.jetbrains.annotations.Nullable; 047import revxrsal.commands.bukkit.actor.BukkitCommandActor; 048import revxrsal.commands.command.CommandActor; 049 050import java.io.InputStream; 051import java.util.Map; 052import java.util.Optional; 053import java.util.Set; 054import java.util.UUID; 055import java.util.stream.Collectors; 056 057/** 058 * BukkitServerProvider 059 * 060 * @author creatorfromhell 061 * @since 0.1.2.0 062 */ 063public class PaperServerProvider implements ServerConnector { 064 065 protected final PaperProxyProvider proxy = new PaperProxyProvider(); 066 protected final PaperScheduler scheduler; 067 protected PaperCalculationsProvider calc; 068 protected String world = null; 069 070 public PaperServerProvider() { 071 072 this(new PaperCalculationsProvider()); 073 } 074 075 public PaperServerProvider(final PaperCalculationsProvider calc) { 076 077 this.calc = calc; 078 this.scheduler = new PaperScheduler(); 079 } 080 081 public PaperServerProvider(final PaperScheduler scheduler) { 082 083 this.scheduler = scheduler; 084 } 085 086 public void setDefaultWorld(final String world) { 087 088 this.world = world; 089 } 090 091 @Override 092 public String name() { 093 094 return "paper"; 095 } 096 097 /** 098 * Finds a WorldProvider object based on the provided world name. 099 * 100 * @param world The name of the world to search for. 101 * 102 * @return An Optional containing the located WorldProvider object if found, or an empty Optional 103 * otherwise. 104 */ 105 @Override 106 public Optional<WorldProvider> findWorld(final String world) { 107 108 final World worldObj = Bukkit.getWorld(world); 109 if(worldObj == null) { 110 return Optional.empty(); 111 } 112 113 return Optional.of(new PaperWorldProvider(worldObj)); 114 } 115 116 /** 117 * Used to replace placeholders from a string. 118 * 119 * @param player The player to use for the placeholder replacement. 120 * @param message The message to replace placeholders in. 121 * 122 * @return The string after placeholders have been replaced. 123 */ 124 @Override 125 public String replacePlaceholder(final UUID player, final String message) { 126 127 if(player == null) return message; 128 129 final Optional<PlayerProvider> playerOpt = PluginCore.server().findPlayer(player); 130 if(Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") && playerOpt.isPresent() 131 && playerOpt.get() instanceof final PaperPlayerProvider bukkitPlayer) { 132 133 return PAPIParser.parse(bukkitPlayer, message); 134 } 135 return message; 136 } 137 138 /** 139 * The proxy provider to use for this implementation. 140 * 141 * @return The proxy provider to use for this implementation. 142 */ 143 @Override 144 public ProxyProvider proxy() { 145 146 return proxy; 147 } 148 149 /** 150 * Used to convert an {@link CommandActor} to a {@link CmdSource}. 151 * 152 * @param actor The command actor. 153 * 154 * @return The {@link CmdSource} for this actor. 155 */ 156 @Override 157 public CmdSource<?> source(@NotNull final CommandActor actor) { 158 159 return new PaperCMDSource((BukkitCommandActor)actor); 160 } 161 162 /** 163 * Retrieves a set of online player names. 164 * 165 * @return Set of online player names. 166 */ 167 @Override 168 public Set<String> onlinePlayersList() { 169 170 return Bukkit.getOnlinePlayers().stream() 171 .map(Player::getName) 172 .collect(Collectors.toSet()); 173 } 174 175 /** 176 * Used to get the amount of online players. 177 * 178 * @return The amount of online players. 179 */ 180 @Override 181 public int onlinePlayers() { 182 183 return Bukkit.getOnlinePlayers().size(); 184 } 185 186 /** 187 * Attempts to find a {@link PlayerProvider player} based on an {@link UUID identifier}. 188 * 189 * @param identifier The identifier 190 * 191 * @return An Optional containing the located {@link PlayerProvider player}, or an empty Optional 192 * if no player is located. 193 */ 194 @Override 195 public Optional<PlayerProvider> findPlayer(@NotNull final UUID identifier) { 196 197 return Optional.of(PaperPlayerProvider.find(identifier.toString())); 198 } 199 200 /** 201 * This is used to return an instance of an {@link PlayerProvider player} based on the provided 202 * instance's player object. 203 * 204 * @param player The instance of the player. 205 * 206 * @return The initialized {@link PlayerProvider player object}. 207 */ 208 @Override 209 public PlayerProvider initializePlayer(@NotNull final Object player) { 210 211 if(player instanceof final Player playerObj) { 212 return new PaperPlayerProvider(playerObj); 213 } 214 return null; 215 } 216 217 /** 218 * Creates a custom texture for a specific UUID. 219 * 220 * @param identifier The UUID to create the custom texture for. 221 * @param username The username of the profile for which the custom texture is being created. 222 * @param texture The custom texture data to apply to the profile. 223 */ 224 @Override 225 public void createCustomTexture(final UUID identifier, final String username, final String texture) { 226 227 } 228 229 /** 230 * Used to determine if this player has played on this server before. 231 * 232 * @param uuid The {@link UUID} that is associated with the player. 233 * 234 * @return True if the player has played on the server before, otherwise false. 235 */ 236 @Override 237 public boolean playedBefore(final UUID uuid) { 238 239 final OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); 240 return player.hasPlayedBefore(); 241 } 242 243 /** 244 * Used to determine if a player with the specified username has played before. 245 * 246 * @param name The username to search for. 247 * 248 * @return True if someone with the specified username has played before, otherwise false. 249 */ 250 @Override 251 public boolean playedBefore(final String name) { 252 253 final OfflinePlayer player = Bukkit.getOfflinePlayer(name); 254 return player.hasPlayedBefore(); 255 } 256 257 /** 258 * Used to determine if a player with the specified username is online. 259 * 260 * @param name The username to search for. 261 * 262 * @return True if someone with the specified username is online. 263 */ 264 @Override 265 public boolean online(final String name) { 266 267 try { 268 269 final UUID id = UUID.fromString(name); 270 return Bukkit.getPlayer(id) != null; 271 } catch(final Exception ignore) { 272 return Bukkit.getPlayer(name) != null; 273 } 274 } 275 276 @Override 277 public Optional<UUID> fromName(final String name) { 278 279 for(final OfflinePlayer player : Bukkit.getServer().getOfflinePlayers()) { 280 if(player.getName() == null) continue; 281 if(player.getName().equalsIgnoreCase(name)) { 282 return Optional.of(player.getUniqueId()); 283 } 284 } 285 return Optional.empty(); 286 } 287 288 /** 289 * Used to locate a username for a specific name. This could be called from either a primary or 290 * secondary thread, and should not call back to the Mojang API ever. 291 * 292 * @param id The {@link UUID} to use for the search. 293 * 294 * @return An optional containing the name if exists, otherwise false. 295 */ 296 @Override 297 public Optional<String> fromID(final UUID id) { 298 299 for(final OfflinePlayer player : Bukkit.getServer().getOfflinePlayers()) { 300 if(player.getUniqueId().equals(id)) { 301 return Optional.ofNullable(player.getName()); 302 } 303 } 304 return Optional.empty(); 305 } 306 307 /** 308 * Returns the name of the default world. 309 * 310 * @return The name of the default world. 311 */ 312 @Override 313 public String defaultWorld() { 314 315 if(world == null) { 316 world = Bukkit.getServer().getWorlds().get(0).getName(); 317 } 318 return world; 319 /* 320 321 if(world == null) { 322 323 final Properties props = new Properties(); 324 try { 325 326 props.load(new FileInputStream(new File(".", "server.properties"))); 327 } catch(IOException ignore) { 328 } 329 world = props.getProperty("level-name"); 330 } 331 return world;*/ 332 } 333 334 /** 335 * Determines if a plugin with the correlating name is currently installed. 336 * 337 * @param name The name to use for our check. 338 * 339 * @return True if a plugin with that name exists, otherwise false. 340 */ 341 @Override 342 public boolean pluginAvailable(final String name) { 343 344 return Bukkit.getPluginManager().isPluginEnabled(name); 345 } 346 347 /** 348 * Used to replace colour codes in a string. 349 * 350 * @param string The string to format. 351 * @param strip If true, the color codes are striped from the string. 352 * 353 * @return The formatted string. 354 */ 355 @Override 356 public String replaceColours(final String string, final boolean strip) { 357 358 if(strip) { 359 return ChatColor.stripColor(string); 360 } 361 return ChatColor.translateAlternateColorCodes('&', string); 362 } 363 364 @Override 365 public AbstractItemStack<?> stackBuilder() { 366 367 return new PaperItemStack(); 368 } 369 370 @Override 371 public void saveResource(final String path, final boolean replace) { 372 373 PaperPluginCore.instance().getPlugin().saveResource(path, replace); 374 } 375 376 /** 377 * Retrieves an input stream for the specified filename from the resources. 378 * 379 * @param filename The name of the file to retrieve from the resources. Cannot be null. 380 * 381 * @return An input stream for the specified file, or null if the file is not found in the 382 * resources. 383 */ 384 @Override 385 public @Nullable InputStream getResource(@NotNull final String filename) { 386 387 return PaperPluginCore.instance().getPlugin().getResource(filename); 388 } 389 390 /** 391 * Provides this implementation's {@link SchedulerProvider scheduler}. 392 * 393 * @return The scheduler for this implementation. 394 */ 395 @Override 396 public SchedulerProvider<?> scheduler() { 397 398 return scheduler; 399 } 400 401 /** 402 * Used to register a crafting recipe to the server. 403 * 404 * @param key The key for the crafting recipe to be registered. 405 * @param recipe The crafting recipe to register. 406 * 407 * @see CraftingRecipe 408 */ 409 @Override 410 public void registerCrafting(@NotNull final String key, @NotNull final CraftingRecipe recipe) { 411 412 if(recipe.isShaped()) { 413 ShapedRecipe shaped; 414 415 try { 416 shaped = new ShapedRecipe(new NamespacedKey(PaperPluginCore.instance().getPlugin(), key), (ItemStack)recipe.getResult().cacheLocale()); 417 } catch(final Exception ignore) { 418 shaped = new ShapedRecipe((ItemStack)recipe.getResult().cacheLocale()); 419 } 420 421 shaped.shape(recipe.getRows()); 422 423 for(final Map.Entry<Character, String> ingredient : recipe.getIngredients().entrySet()) { 424 shaped.setIngredient(ingredient.getKey(), Material.valueOf(ingredient.getValue())); 425 } 426 Bukkit.getServer().addRecipe(shaped); 427 } else { 428 ShapelessRecipe shapeless; 429 430 try { 431 shapeless = new ShapelessRecipe(new NamespacedKey(PaperPluginCore.instance().getPlugin(), key), (ItemStack)recipe.getResult().cacheLocale()); 432 } catch(final Exception ignore) { 433 shapeless = new ShapelessRecipe((ItemStack)recipe.getResult().cacheLocale()); 434 } 435 436 for(final Map.Entry<Character, String> ingredient : recipe.getIngredients().entrySet()) { 437 shapeless.addIngredient(1, Material.valueOf(ingredient.getValue())); 438 } 439 Bukkit.getServer().addRecipe(shapeless); 440 } 441 } 442 443 @Override 444 public PaperCalculationsProvider calculations() { 445 446 return calc; 447 } 448}