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