001package net.tnemc.plugincore.sponge.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.plugincore.core.compatibility.CmdSource; 023import net.tnemc.plugincore.core.compatibility.LogProvider; 024import net.tnemc.plugincore.core.compatibility.PlayerProvider; 025import net.tnemc.plugincore.core.compatibility.ProxyProvider; 026import net.tnemc.plugincore.core.compatibility.ServerConnector; 027import net.tnemc.plugincore.core.compatibility.WorldProvider; 028import net.tnemc.plugincore.core.compatibility.helper.CraftingRecipe; 029import net.tnemc.plugincore.core.compatibility.scheduler.SchedulerProvider; 030import net.tnemc.plugincore.sponge.SpongePluginCore; 031import net.tnemc.plugincore.sponge.impl.scheduler.SpongeScheduler; 032import net.tnemc.sponge.SpongeItemCalculationsProvider; 033import net.tnemc.sponge.SpongeItemStack; 034import org.jetbrains.annotations.NotNull; 035import org.jetbrains.annotations.Nullable; 036import org.spongepowered.api.ResourceKey; 037import org.spongepowered.api.Sponge; 038import org.spongepowered.api.entity.living.player.server.ServerPlayer; 039import org.spongepowered.api.util.Nameable; 040import org.spongepowered.api.world.DefaultWorldKeys; 041import org.spongepowered.api.world.server.ServerWorld; 042import revxrsal.commands.command.CommandActor; 043import revxrsal.commands.sponge.actor.SpongeCommandActor; 044 045import java.io.File; 046import java.io.FileOutputStream; 047import java.io.IOException; 048import java.io.InputStream; 049import java.io.OutputStream; 050import java.net.URL; 051import java.net.URLConnection; 052import java.util.Optional; 053import java.util.Set; 054import java.util.UUID; 055import java.util.stream.Collectors; 056 057/** 058 * SpongeServerProvider 059 * 060 * @author creatorfromhell 061 * @since 0.1.2.0 062 */ 063public class SpongeServerProvider implements ServerConnector { 064 065 private final SpongeItemCalculationsProvider calc = new SpongeItemCalculationsProvider(); 066 private final SpongeProxyProvider proxy = new SpongeProxyProvider(); 067 068 private final SpongeScheduler scheduler; 069 070 protected String world = null; 071 072 public SpongeServerProvider() { 073 074 this.scheduler = new SpongeScheduler(); 075 } 076 077 @Override 078 public String name() { 079 080 return "sponge"; 081 } 082 083 /** 084 * Finds a WorldProvider object based on the provided world name. 085 * 086 * @param world The name of the world to search for. 087 * 088 * @return An Optional containing the located WorldProvider object if found, or an empty Optional 089 * otherwise. 090 */ 091 @Override 092 public Optional<WorldProvider> findWorld(final String world) { 093 094 final Optional<ServerWorld> worldOptional = Sponge.server().worldManager().world(ResourceKey.resolve(world)); 095 return worldOptional.map(SpongeWorldProvider::new); 096 } 097 098 /** 099 * Used to replace placeholders from a string. 100 * 101 * @param player The player to use for the placeholder replacement. 102 * @param message The message to replace placeholders in. 103 * 104 * @return The string after placeholders have been replaced. 105 */ 106 @Override 107 public String replacePlaceholder(final UUID player, final String 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 120 return proxy; 121 } 122 123 /** 124 * Used to convert an {@link CommandActor} to a {@link CmdSource}. 125 * 126 * @param actor The command actor. 127 * 128 * @return The {@link CmdSource} for this actor. 129 */ 130 @Override 131 public CmdSource<?> source(@NotNull final CommandActor actor) { 132 133 return new SpongeCMDSource((SpongeCommandActor)actor); 134 } 135 136 /** 137 * Retrieves a set of online player names. 138 * 139 * @return Set of online player names. 140 */ 141 @Override 142 public Set<String> onlinePlayersList() { 143 144 return Sponge.server().onlinePlayers().stream() 145 .map(Nameable::name) 146 .collect(Collectors.toSet()); 147 } 148 149 /** 150 * Used to get the amount of online players. 151 * 152 * @return The amount of online players. 153 */ 154 @Override 155 public int onlinePlayers() { 156 157 return Sponge.server().onlinePlayers().size(); 158 } 159 160 /** 161 * Attempts to find a {@link PlayerProvider player} based on an {@link UUID identifier}. 162 * 163 * @param identifier The identifier 164 * 165 * @return An Optional containing the located {@link PlayerProvider player}, or an empty Optional 166 * if no player is located. 167 */ 168 @Override 169 public Optional<PlayerProvider> findPlayer(@NotNull final UUID identifier) { 170 171 final Optional<ServerPlayer> player = Sponge.server().player(identifier); 172 return player.map(value->new SpongePlayerProvider(value.user(), SpongePluginCore.instance().getContainer())); 173 } 174 175 /** 176 * This is used to return an instance of an {@link PlayerProvider player} based on the provided 177 * instance's player object. 178 * 179 * @param player The instance of the player. 180 * 181 * @return The initialized {@link PlayerProvider player object}. 182 */ 183 @Override 184 public PlayerProvider initializePlayer(@NotNull final Object player) { 185 186 if(player instanceof final ServerPlayer playerObj) { 187 return new SpongePlayerProvider(playerObj.user(), SpongePluginCore.instance().getContainer()); 188 } 189 return null; 190 } 191 192 /** 193 * Creates a custom texture for a specific UUID. 194 * 195 * @param identifier The UUID to create the custom texture for. 196 * @param username The username of the profile for which the custom texture is being created. 197 * @param texture The custom texture data to apply to the profile. 198 */ 199 @Override 200 public void createCustomTexture(final UUID identifier, final String username, final String texture) { 201 //TODO: Implement 202 } 203 204 /** 205 * Used to determine if this player has played on this server before. 206 * 207 * @param identifier The {@link UUID} that is associated with the player. 208 * 209 * @return True if the player has played on the server before, otherwise false. 210 */ 211 @Override 212 public boolean playedBefore(final UUID identifier) { 213 214 final Optional<ServerPlayer> player = Sponge.server().player(identifier); 215 return player.map(ServerPlayer::hasPlayedBefore).orElse(false); 216 } 217 218 /** 219 * Used to determine if a player with the specified username has played before. 220 * 221 * @param name The username to search for. 222 * 223 * @return True if someone with the specified username has played before, otherwise false. 224 */ 225 @Override 226 public boolean playedBefore(final String name) { 227 228 final Optional<ServerPlayer> player = Sponge.server().player(name); 229 return player.map(ServerPlayer::hasPlayedBefore).orElse(false); 230 } 231 232 /** 233 * Used to determine if a player with the specified username is online. 234 * 235 * @param name The username to search for. 236 * 237 * @return True if someone with the specified username is online. 238 */ 239 @Override 240 public boolean online(final String name) { 241 242 try { 243 244 final Optional<ServerPlayer> player = Sponge.server().player(UUID.fromString(name)); 245 return player.map(ServerPlayer::isOnline).orElse(false); 246 } catch(final Exception e) { 247 248 final Optional<ServerPlayer> player = Sponge.server().player(name); 249 return player.map(ServerPlayer::isOnline).orElse(false); 250 } 251 } 252 253 @Override 254 public Optional<UUID> fromName(final String name) { 255 256 return Optional.empty(); 257 } 258 259 /** 260 * Used to locate a username for a specific name. This could be called from either a primary or 261 * secondary thread, and should not call back to the Mojang API ever. 262 * 263 * @param id The {@link UUID} to use for the search. 264 * 265 * @return An optional containing the name if exists, otherwise false. 266 */ 267 @Override 268 public Optional<String> fromID(final UUID id) { 269 270 return Optional.empty(); 271 } 272 273 /** 274 * Returns the name of the default world. 275 * 276 * @return The name of the default world. 277 */ 278 @Override 279 public String defaultWorld() { 280 281 if(world == null) { 282 world = Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get().key().asString(); 283 } 284 return world; 285 } 286 287 /** 288 * Used to replace colour codes in a string. 289 * 290 * @param string The string to format. 291 * @param strip If true, the color codes are striped from the string. 292 * 293 * @return The formatted string. 294 */ 295 @Override 296 public String replaceColours(final String string, final boolean strip) { 297 298 return string; 299 } 300 301 @Override 302 public AbstractItemStack<?> stackBuilder() { 303 304 return new SpongeItemStack(); 305 } 306 307 @Override 308 public void saveResource(String resourcePath, final boolean replace) { 309 310 if(resourcePath != null && !resourcePath.isEmpty()) { 311 312 resourcePath = resourcePath.replace('\\', '/'); 313 314 final LogProvider logger = SpongePluginCore.log(); 315 final InputStream in = this.getResource(resourcePath); 316 if(in == null) { 317 318 throw new IllegalArgumentException("The embedded resource '" + resourcePath + "' cannot be found in the jar."); 319 } else { 320 321 final File outFile = new File(SpongePluginCore.directory(), resourcePath); 322 final int lastIndex = resourcePath.lastIndexOf(47); 323 final File outDir = new File(SpongePluginCore.directory(), resourcePath.substring(0, Math.max(lastIndex, 0))); 324 if(!outDir.exists()) { 325 outDir.mkdirs(); 326 } 327 328 try { 329 if(outFile.exists() && !replace) { 330 } else { 331 332 final OutputStream out = new FileOutputStream(outFile); 333 final byte[] buf = new byte[1024]; 334 335 int len; 336 while((len = in.read(buf)) > 0) { 337 out.write(buf, 0, len); 338 } 339 340 out.close(); 341 in.close(); 342 } 343 } catch(final IOException ignore) { 344 logger.error("Could not save " + outFile.getName() + " to " + outFile); 345 } 346 347 } 348 } else { 349 throw new IllegalArgumentException("ResourcePath cannot be null or empty"); 350 } 351 } 352 353 /** 354 * Provides this implementation's {@link SchedulerProvider scheduler}. 355 * 356 * @return The scheduler for this implementation. 357 */ 358 @Override 359 public SpongeScheduler scheduler() { 360 361 return scheduler; 362 } 363 364 /** 365 * Used to register a crafting recipe to the server. 366 * 367 * @param key The key for the crafting recipe to be registered. 368 * @param recipe The crafting recipe to register. 369 * 370 * @see CraftingRecipe 371 */ 372 @Override 373 public void registerCrafting(@NotNull final String key, @NotNull final CraftingRecipe recipe) { 374 //TODO: Sponge Register crafting 375 } 376 377 @Override 378 public SpongeItemCalculationsProvider calculations() { 379 380 return calc; 381 } 382 383 @Override 384 public @Nullable InputStream getResource(@NotNull final String filename) { 385 386 try { 387 final URL url = this.getClass().getClassLoader().getResource(filename); 388 if(url == null) { 389 return null; 390 } else { 391 final URLConnection connection = url.openConnection(); 392 connection.setUseCaches(false); 393 return connection.getInputStream(); 394 } 395 } catch(final IOException var4) { 396 return null; 397 } 398 } 399}