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}