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.helper.CraftingRecipe; 033import net.tnemc.plugincore.core.compatibility.scheduler.SchedulerProvider; 034import org.bukkit.Bukkit; 035import org.bukkit.ChatColor; 036import org.bukkit.Material; 037import org.bukkit.NamespacedKey; 038import org.bukkit.OfflinePlayer; 039import org.bukkit.entity.Player; 040import org.bukkit.inventory.ItemStack; 041import org.bukkit.inventory.ShapedRecipe; 042import org.bukkit.inventory.ShapelessRecipe; 043import org.jetbrains.annotations.NotNull; 044import org.jetbrains.annotations.Nullable; 045import revxrsal.commands.bukkit.actor.BukkitCommandActor; 046import revxrsal.commands.command.CommandActor; 047 048import java.io.InputStream; 049import java.util.Map; 050import java.util.Optional; 051import java.util.UUID; 052 053/** 054 * BukkitServerProvider 055 * 056 * @author creatorfromhell 057 * @since 0.1.2.0 058 */ 059public class BukkitServerProvider implements ServerConnector { 060 061 protected final BukkitCalculationsProvider calc = new BukkitCalculationsProvider(); 062 protected final BukkitProxyProvider proxy = new BukkitProxyProvider(); 063 064 protected final BukkitScheduler scheduler; 065 066 protected String world = null; 067 068 public BukkitServerProvider() { 069 this.scheduler = new BukkitScheduler(); 070 } 071 072 public BukkitServerProvider(final BukkitScheduler scheduler) { 073 this.scheduler = scheduler; 074 } 075 076 public void setDefaultWorld(final String world) { 077 this.world = world; 078 } 079 080 @Override 081 public String name() { 082 return "bukkit"; 083 } 084 085 /** 086 * Used to replace placeholders from a string. 087 * 088 * @param player The player to use for the placeholder replacement. 089 * @param message The message to replace placeholders in. 090 * 091 * @return The string after placeholders have been replaced. 092 */ 093 @Override 094 public String replacePlaceholder(final UUID player, final String message) { 095 096 if(player == null) return message; 097 098 final Optional<PlayerProvider> playerOpt = PluginCore.server().findPlayer(player); 099 if(Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI") && playerOpt.isPresent() 100 && playerOpt.get() instanceof final BukkitPlayerProvider bukkitPlayer) { 101 102 return PAPIParser.parse(bukkitPlayer, message); 103 } 104 return message; 105 } 106 107 /** 108 * The proxy provider to use for this implementation. 109 * 110 * @return The proxy provider to use for this implementation. 111 */ 112 @Override 113 public ProxyProvider proxy() { 114 return proxy; 115 } 116 117 /** 118 * Used to convert an {@link CommandActor} to a {@link CmdSource}. 119 * 120 * @param actor The command actor. 121 * 122 * @return The {@link CmdSource} for this actor. 123 */ 124 @Override 125 public CmdSource<?> source(@NotNull final CommandActor actor) { 126 return new BukkitCMDSource((BukkitCommandActor)actor); 127 } 128 129 /** 130 * Used to get the amount of online players. 131 * 132 * @return The amount of online players. 133 */ 134 @Override 135 public int onlinePlayers() { 136 return Bukkit.getOnlinePlayers().size(); 137 } 138 139 /** 140 * Attempts to find a {@link PlayerProvider player} based on an {@link UUID identifier}. 141 * 142 * @param identifier The identifier 143 * 144 * @return An Optional containing the located {@link PlayerProvider player}, or an empty 145 * Optional if no player is located. 146 */ 147 @Override 148 public Optional<PlayerProvider> findPlayer(@NotNull final UUID identifier) { 149 150 return Optional.of(BukkitPlayerProvider.find(identifier.toString())); 151 } 152 153 /** 154 * This is used to return an instance of an {@link PlayerProvider player} based on the provided 155 * instance's player object. 156 * 157 * @param player The instance of the player. 158 * 159 * @return The initialized {@link PlayerProvider player object}. 160 */ 161 @Override 162 public PlayerProvider initializePlayer(@NotNull final Object player) { 163 if(player instanceof final Player playerObj) { 164 return new BukkitPlayerProvider(playerObj); 165 } 166 return null; 167 } 168 169 /** 170 * Used to determine if this player has played on this server before. 171 * 172 * @param uuid The {@link UUID} that is associated with the player. 173 * 174 * @return True if the player has played on the server before, otherwise false. 175 */ 176 @Override 177 public boolean playedBefore(final UUID uuid) { 178 179 final OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); 180 return player.hasPlayedBefore(); 181 } 182 183 /** 184 * Used to determine if a player with the specified username has played 185 * before. 186 * 187 * @param name The username to search for. 188 * 189 * @return True if someone with the specified username has played before, 190 * otherwise false. 191 */ 192 @Override 193 public boolean playedBefore(final String name) { 194 195 final OfflinePlayer player = Bukkit.getOfflinePlayer(name); 196 return player.hasPlayedBefore(); 197 } 198 199 /** 200 * Used to determine if a player with the specified username is online. 201 * 202 * @param name The username to search for. 203 * 204 * @return True if someone with the specified username is online. 205 */ 206 @Override 207 public boolean online(final String name) { 208 try { 209 210 final UUID id = UUID.fromString(name); 211 return Bukkit.getPlayer(id) != null; 212 } catch(final Exception ignore) { 213 return Bukkit.getPlayer(name) != null; 214 } 215 } 216 217 @Override 218 public Optional<UUID> fromName(final String name) { 219 for(final OfflinePlayer player : Bukkit.getServer().getOfflinePlayers()) { 220 if(player.getName() == null) continue; 221 if(player.getName().equalsIgnoreCase(name)) { 222 return Optional.of(player.getUniqueId()); 223 } 224 } 225 return Optional.empty(); 226 } 227 228 /** 229 * Used to locate a username for a specific name. This could be called from either a primary or 230 * secondary thread, and should not call back to the Mojang API ever. 231 * 232 * @param id The {@link UUID} to use for the search. 233 * 234 * @return An optional containing the name if exists, otherwise false. 235 */ 236 @Override 237 public Optional<String> fromID(final UUID id) { 238 for(final OfflinePlayer player : Bukkit.getServer().getOfflinePlayers()) { 239 if(player.getUniqueId().equals(id)) { 240 return Optional.ofNullable(player.getName()); 241 } 242 } 243 return Optional.empty(); 244 } 245 246 /** 247 * Returns the name of the default world. 248 * 249 * @return The name of the default world. 250 */ 251 @Override 252 public String defaultWorld() { 253 if(world == null) { 254 world = Bukkit.getServer().getWorlds().get(0).getName(); 255 } 256 return world; 257 /* 258 259 if(world == null) { 260 261 final Properties props = new Properties(); 262 try { 263 264 props.load(new FileInputStream(new File(".", "server.properties"))); 265 } catch(IOException ignore) { 266 } 267 world = props.getProperty("level-name"); 268 } 269 return world;*/ 270 } 271 272 /** 273 * Determines if a plugin with the correlating name is currently installed. 274 * 275 * @param name The name to use for our check. 276 * 277 * @return True if a plugin with that name exists, otherwise false. 278 */ 279 @Override 280 public boolean pluginAvailable(final String name) { 281 return Bukkit.getPluginManager().isPluginEnabled(name); 282 } 283 284 /** 285 * Used to replace colour codes in a string. 286 * @param string The string to format. 287 * @param strip If true, the color codes are striped from the string. 288 * @return The formatted string. 289 */ 290 @Override 291 public String replaceColours(final String string, final boolean strip) { 292 if(strip) { 293 return ChatColor.stripColor(string); 294 } 295 return ChatColor.translateAlternateColorCodes('&', string); 296 } 297 298 @Override 299 public AbstractItemStack<?> stackBuilder() { 300 return new BukkitItemStack(); 301 } 302 303 @Override 304 public void saveResource(final String path, final boolean replace) { 305 BukkitPluginCore.instance().getPlugin().saveResource(path, replace); 306 } 307 308 /** 309 * Retrieves an input stream for the specified filename from the resources. 310 * 311 * @param filename The name of the file to retrieve from the resources. Cannot be null. 312 * 313 * @return An input stream for the specified file, or null if the file is not found in the 314 * resources. 315 */ 316 @Override 317 public @Nullable InputStream getResource(@NotNull final String filename) { 318 return BukkitPluginCore.instance().getPlugin().getResource(filename); 319 } 320 321 /** 322 * Provides this implementation's {@link SchedulerProvider scheduler}. 323 * 324 * @return The scheduler for this implementation. 325 */ 326 @Override 327 public SchedulerProvider<?> scheduler() { 328 return scheduler; 329 } 330 331 /** 332 * Used to register a crafting recipe to the server. 333 * 334 * @param key The key for the crafting recipe to be registered. 335 * @param recipe The crafting recipe to register. 336 * 337 * @see CraftingRecipe 338 */ 339 @Override 340 public void registerCrafting(@NotNull final String key, @NotNull final CraftingRecipe recipe) { 341 if(recipe.isShaped()) { 342 ShapedRecipe shaped; 343 344 try { 345 shaped = new ShapedRecipe(new NamespacedKey(BukkitPluginCore.instance().getPlugin(), key), (ItemStack)recipe.getResult().locale()); 346 } catch(final Exception ignore) { 347 shaped = new ShapedRecipe((ItemStack)recipe.getResult().locale()); 348 } 349 350 shaped.shape(recipe.getRows()); 351 352 for(final Map.Entry<Character, String> ingredient : recipe.getIngredients().entrySet()) { 353 shaped.setIngredient(ingredient.getKey(), Material.valueOf(ingredient.getValue())); 354 } 355 Bukkit.getServer().addRecipe(shaped); 356 } else { 357 ShapelessRecipe shapeless; 358 359 try { 360 shapeless = new ShapelessRecipe(new NamespacedKey(BukkitPluginCore.instance().getPlugin(), key), (ItemStack)recipe.getResult().locale()); 361 } catch(final Exception ignore) { 362 shapeless = new ShapelessRecipe((ItemStack)recipe.getResult().locale()); 363 } 364 365 for(final Map.Entry<Character, String> ingredient : recipe.getIngredients().entrySet()) { 366 shapeless.addIngredient(1, Material.valueOf(ingredient.getValue())); 367 } 368 Bukkit.getServer().addRecipe(shapeless); 369 } 370 } 371 372 @Override 373 public BukkitCalculationsProvider calculations() { 374 return calc; 375 } 376}