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